SMF 2 и ldap

Автор ops2012, 18 января 2012, 17:13:32

« назад - далее »

0 Пользователи и 1 гость просматривают эту тему.

ops2012

Добрый день.
Кто-нибудь смог интегрировать smf2 и ldap( active directory )? Необходимая вещь для корпоративных форумов, а нету. Единственный мод был написан psa и только под первую версию. Сейчас я допилил до работоспособности авторизацию на smf2. Взять архив можно тут http://ifolder.ru/28178991 . Там оригинальные файлы от psa, исправленный LdapAuth.php под вторую версию, diff для нескольких файлов форума.
Кто-нибудь может помочь со всем этим делом? php я второй раз в жизни крутил, наверняка ошибся где-то. Если по пунктам, в чём нужна помощь:

  • Посмотреть сам код - он вообще нормальный или нет.
  • Не сохраняются ldap settings из админки почему-то. Что исправлять.
  • Прикрутить кастомные поля в профиль. Так что бы в админке можно было указывать какое поле брать из ldap ( например тот же location, telephone,...).
  • Возможно надо выкинуть кусок отвечающий за возможность из админки завести пользователя ldap?
  • Собрать из этот нормальный мод, как минимум.
По хорошему, конечно, надо интегрировать ldap в smf. Кто-нибудь общался на эту тему с разработчиками?

Это ещё только начальный этап. После нормального решения у меня есть желание сделать авторизацию через керберос - SSO это очень хорошая вещь.

ops2012

Код я допилил и добавил возможность использовать ldap uid ( в MSAD это objetsid). Самый простой вариант - добавил  колонку в таблицу members.
Может кто расскажет как правильней кастомные поля прикрутить? Правильно я понимаю что эти поля хранятся в таблице themes? Почему не сделали отдельную таблицу под это дело, похожую по структуре на таблицу members? Было бы проще, или тут я ошибаюсь? Мне, по хорошему, надо добавлять кастомное поле и где-то указывать из каких атрибутов ldap его собирать. В какую таблицу это засунуть, что бы было правильно?

ops2012

Ok. Сделал поддержку kerberos+MSAD - кривовато похоже, но пойдёт. Если никому не интересно пойду в соседнем "отделе" узнаю надо ли это.

Mavn

выложили бы здесь файлы и инструкцию куда чего писать тогда думаю было бы интересно кому то
SimpleMachines Russian Community Team
п.1 Пройду курсы гадалок для определения исходного кода по скриншоту.

п.2 У вас нет желания читать правила раздела, у меня нет желания одобрять темы, которые не соответствуют этим правилам.

ops2012

Ок. Просто с керберосом были ещё заморочки.
Итак приступим. По LDAP: для начала сам файл LdapAuth.php , который надо кинуть в каталог Source
<?php

// ldapauth_enable -- will prevent anything from being done if not set
// ldapauth_serverurl -- 'ldap://yourldapserver';
// ldapauth_usersuffix -- with MSAD will be set to '@yourdomain.yourtld';
// ldapauth_userprefix -- useful in conjunction with ldapauth_usersuffix to create a dn-based login
// ldapauth_searchdn -- 'OU=Your Users,DC=yourdomain,DC=yourtld';
// ldapauth_searchKey -- for MSAD will be 'sAMAccountName';
// ldapauth_emailuselogin -- set to indicate that ldapauth_emailsuffix should be used 
// ldapauth_emailsuffix -- added to the login username to create the users' email addresses
// ldapauth_emailattr -- if not using ldapauth_emailsuffix, the attribute in ldap to be used for the email addresses
// ldapauth_locationuseou -- use the top level ou as the users' locations
// ldapauth_locationattr -- if not using ldapauth_locationuseou, the attribute in ldap to be used for the locations
// ldapauth_updateonlogin -- set to indicate that the user's information should be updated on every login
// ldapauth_fullnameattr -- the attribute in ldap to be used for the fullname (cn for MSAD); required
// ldapauth_regresnames -- set to indicate that ldap auth should autoregister/update users with reserved name logins
// ldapauth_authresnames -- set to indicate that ldap auth should authenticate users with reserved name logins
// ldapauth_passwdindb -- set to indicate that ldap passwords should be stored in the local database
// ldapauth_uidattr -- the attribute in ldap to be used for the UID (objectsid for MSAD)

//TODO much error handling, especially where connections and queries fail, or the user has bad or missing settings

function smf_ldap_auth($username$password$seconds)
{
global $db_prefix$user_info$modSettings$func$txt$sourcedir$smcFunc;
require_once($sourcedir '/Subs-Members.php');
if (!isset($modSettings['ldapauth_enable']) || !$modSettings['ldapauth_enable'])
return false;
if ((!isset($modSettings['ldapauth_authresnames']) || !$modSettings['ldapauth_authresnames']) && isReservedName($username))
return false;
// We can use the password passed in since we abandoned the integration hack
$thepasswrd $password;
$lattributes explode(','$modSettings['ldapauth_fullnameattr']);
if (isset($modSettings['ldapauth_emailattr']) && !empty($modSettings['ldapauth_emailattr']))
$lattributes[] = $modSettings['ldapauth_emailattr'];
if (isset($modSettings['ldapauth_locationattr']) && !empty($modSettings['ldapauth_locationattr']))
$lattributes[] = $modSettings['ldapauth_locationattr'];
if (isset($modSettings['ldapauth_uidattr']) && !empty($modSettings['ldapauth_uidattr']))
$lattributes[] = $modSettings['ldapauth_uidattr'];

$sha_passwd sha1(strtolower($username) . un_htmlspecialchars(stripslashes($thepasswrd)));
if ($lds ldap_connect($modSettings['ldapauth_serverurl']))
{
// these next two are required for recent versions of MSAD, 
// but may need tweak options for other ldap servers
ldap_set_option($ldsLDAP_OPT_PROTOCOL_VERSION3);
ldap_set_option($ldsLDAP_OPT_REFERRALS0);

// if using an application account to pre-bind, find the user's DN with the pre-bind account
$userDN "";
if (isset($modSettings['ldapauth_bindusername']) && $modSettings['ldapauth_bindusername'])
{
if ($bd ldap_bind($lds$modSettings['ldapauth_bindusername'], $modSettings['ldapauth_bindpassword']))
{
log_error("Bound with " $modSettings['ldapauth_bindusername']);
$results=ldap_search($lds$modSettings['ldapauth_searchdn'], "(|({$modSettings['ldapauth_searchkey']}=$username))");
$lentries ldap_get_entries($lds$results);
$found $lentries['count'];
if(!$found// if the user was not found in LDAP
{
log_error("Username $username not found in LDAP.");
return false;
}
else // The user was found in LDAP. So get his DN from ldapauth_locationattr.
{
log_error("Username $username found in LDAP. Continuing...");
$userDN $lentries[0]['dn'];
}
ldap_unbind($lds);
$lds ldap_connect($modSettings['ldapauth_serverurl']);
ldap_set_option($ldsLDAP_OPT_PROTOCOL_VERSION3);
ldap_set_option($ldsLDAP_OPT_REFERRALS0);
}
}
if(!$userDN$userDN $modSettings['ldapauth_userprefix'] . $username $modSettings['ldapauth_usersuffix'];
//clear passwd if we're not going to store it in the db
if (isset($modSettings['ldapauth_passwdindb']) && !$modSettings['ldapauth_passwdindb'])
$sha_passwd "LDAPOnly";
if ($bd ldap_bind($lds,  $userDN$thepasswrd))
{
//login success
log_error('Bound with '.$username);
$lentries ldap_get_entries($ldsldap_search($lds$modSettings['ldapauth_searchdn'], "({$modSettings['ldapauth_searchkey']}=$username)"$lattributes));
//verify user exests by ldap uid and update memeber_name if changed
if (isset($modSettings['ldapauth_uidattr']) && !empty($modSettings['ldapauth_uidattr']))
{
$luid bin_to_str_sid($lentries[0][$modSettings['ldapauth_uidattr']][0]);
$request $smcFunc['db_query']('''
SELECT id_member
FROM {db_prefix}members
WHERE ' 
'ldapattr_uid = {string:lid_member}' '
LIMIT 1'
,
array(
'lid_member' => $luid,
)
);
if (($smcFunc['db_num_rows']($request) > 0) && !strcmp($username$cresult['member_name']))
{
$cresult $smcFunc['db_fetch_assoc']($request);
updateMemberData($cresult['id_member'], array('member_name' => $username));
}
$smcFunc['db_free_result']($request);
}
//verify user exists in forum user database
$request $smcFunc['db_query']('''
SELECT id_member
FROM {db_prefix}members
WHERE ' 
. ($smcFunc['db_case_sensitive'] ? 'LOWER(member_name) = LOWER({string:user_name})' 'member_name = {string:user_name}') . '
LIMIT 1'
,
array(
'user_name' => $smcFunc['db_case_sensitive'] ? strtolower($username) : $username,
)
);
//if this isn't set we won't authenticate a reserved name that isn't already registered
if ((!isset($modSettings['ldapauth_regresnames']) || !$modSettings['ldapauth_regresnames']) && isReservedName($username) && $smcFunc['db_num_rows']($request) == 0)
return false;

//Hairy!
// IF 
// (
//  the user isn't already registered 
//  OR 
// (
// updateonlogin is set 
// AND 
// updateonlogin is true
// )
// )
// AND 
// (
// (
// regresnames is set 
// AND 
// regresname is true
//
// OR 
// the username is NOT reserved
// )
if (($smcFunc['db_num_rows']($request) == || (isset($modSettings['ldapauth_updateonlogin']) && $modSettings['ldapauth_updateonlogin'])) && ((isset($modSettings['ldapauth_regresnames']) && $modSettings['ldapauth_regresnames']) || !isReservedName($username)))
{
// We're going to update or add the user, so setup the data we need

// Get user's full name (and possibly email address & location) from the directory

if (isset($modSettings['ldapauth_locationuseou']) && $modSettings['ldapauth_locationuseou'])
{
// Parse the highest level Organizational Unit as their location
$i1 strrpos($lentries[0]["dn"],'OU=')+3;
$i2 strpos($lentries[0]["dn"],',DC=',$i1);
$llocation substr($lentries[0]["dn"],$i1,$i2-$i1);
}

else if (isset($modSettings['ldapauth_locationattr']) && !empty($modSettings['ldapauth_locationattr'])) {
$llocation '';
$ldapauth_loc explode(','$modSettings['ldapauth_locationattr']);
foreach ($ldapauth_loc as $value) {
$llocation .= $lentries[0][$value][0] . ' ';
}
$llocation chop ($llocation);
 }
else $llocation '';
if (isset($modSettings['ldapauth_emailuselogin']) && $modSettings['ldapauth_emailuselogin']) 
$lmail $username . (isset($modSettings['ldapauth_emailsuffix']) ? $modSettings['ldapauth_emailsuffix'] : '');
else if (isset($modSettings['ldapauth_emailattr']) && !empty($modSettings['ldapauth_emailattr']))
$lmail $lentries[0][$modSettings['ldapauth_emailattr']][0];
else $lmail '';
//email checking courtesy of Subs-Members.php
if (empty($lmail) || preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~'stripslashes($lmail)) === || strlen(stripslashes($lmail)) > 255)
                
{
log_error(sprintf($txt['valid_email_needed'], $username));
$lmail ''; // we will let this go, although SMF doesn't really like missing email addresses
}
$thelocation $smcFunc['htmlspecialchars']($llocation);
$lrealname '';
$ldapauth_rname explode(','$modSettings['ldapauth_fullnameattr']);
foreach ($ldapauth_rname as $value) {
$lrealname .= $lentries[0][$value][0] .' ';
}
$lrealname chop ($lrealname);
$therealname $smcFunc['htmlspecialchars']($lrealname);

if ($smcFunc['db_num_rows']($request) > 0)
{
// User actually exists, so only update ldap-changeable data
$cresult $smcFunc['db_fetch_assoc']($request);
updateMemberData($cresult['id_member'], array('passwd' => $sha_passwd'email_address' => $lmail,'real_name' => $therealname'location' => $thelocation));
ldap_close($lds);
$smcFunc['db_free_result']($request);
// tells LogInOut.php not to check passwd, since we already found it to be good
return true;
} else {
log_error('Not Bound with user ' $username);
}

// User does not exist in SMF database - create
$regOptions = array(
'interface' => 'guest',
'username' => $username,
'email' => $lmail,
'password' => $sha_passwd,
'require' => 'nothing',
'password_check' => $sha_passwd,
'validation_code' => "''",
'check_reserved_name' => true,
'check_password_strength' => false,
'check_email_ban' => true,
'send_welcome_email' => !empty($modSettings['send_welcomeEmail']),
);
$memberID registerMember($regOptions);
updateMemberData($memberID, array('location' => $thelocation'real_name' => $therealname'passwd' => $sha_passwd));
if (isset($modSettings['ldapauth_uidattr']) && !empty($modSettings['ldapauth_uidattr']))
{
$luid bin_to_str_sid($lentries[0][$modSettings['ldapauth_uidattr']][0]);
updateMemberData($memberID, array('ldapattr_uid' => $luid));
}
updateStats('member');

// If it's enabled, increase the registrations for today.
trackStats(array('registers' => '+'));

// Actually, this is the entry, not the results, which are anonymous.  
// Oh well, it'll be freed once the page is served anyway.
}
else
if (!isset($modSettings['ldapauth_passwdindb']) || $modSettings['ldapauth_passwdindb'])
{
// User does exist (or it's reserved and we've set the option not to update data for reserved names, 
// but we'll update the password in case it's changed
$cresult $smcFunc['db_fetch_assoc']($result);
updateMemberData($cresult['id_member'], array('passwd' => $sha_passwd));
}
ldap_close($lds);
$smcFunc['db_free_result']($request);
// tells LogInOut.php not to check passwd, since we already found it to be good
return true;
}
}
ldap_close($lds);

// allow authentication fallthrough to other methods (local database hashes of various kinds, by default)
return false;
}

// Returns the textual SID
function bin_to_str_sid($binsid) {
    
$hex_sid bin2hex($binsid);
    
$rev hexdec(substr($hex_sid02));
    
$subcount hexdec(substr($hex_sid22));
    
$auth hexdec(substr($hex_sid412));
    
$result    "$rev-$auth";

    for (
$x=0;$x $subcount$x++) {
        
$subauth[$x] = 
            
hexdec(little_endian(substr($hex_sid16 + ($x 8), 8)));
        
$result .= "-" $subauth[$x];
    }

    
// Cheat by tacking on the S-
    
return 'S-' $result;
}

// Converts a little-endian hex-number to one, that 'hexdec' can convert
function little_endian($hex) {
    for (
$x strlen($hex) - 2$x >= 0$x $x 2) {
        
$result .= substr($hex$x2);
    }
    return 
$result;
}

?>


ops2012

Прогнанный diff по оригинальному smf-2.0.2 и изменённому, по всем файлам.
diff -u -r /home/user/temp/smf-2.0.2/index.php /var/www/html/forum/index.php
--- /home/user/temp/smf-2.0.2/index.php 2011-12-22 20:12:36.000000000 +0400
+++ /var/www/html/forum/index.php 2012-02-02 12:50:26.137801421 +0400
@@ -168,6 +169,16 @@
// Load the user's cookie (or set as guest) and load their settings.
loadUserSettings();

+// Kerberos hack - autologin
+if ($modSettings['kerbauth_enable'] && $user_info['is_guest'] && isset($_SERVER['REMOTE_USER'])) {
+ list($kerbuser, $realm) = explode("@", strtolower($_SERVER['REMOTE_USER']));
+ $_POST['user'] = $kerbuser;
+ // Hack for empty password in kerberos
+ $_POST['passwrd'] = "LDAPOnly";
+ require_once($sourcedir . '/LogInOut.php');
+ Login2();
+}
+
// Load the current board's information.
loadBoard();

diff -u -r /home/user/temp/smf-2.0.2/Sources/Admin.php /var/www/html/forum/Sources/Admin.php
--- /home/user/temp/smf-2.0.2/Sources/Admin.php 2011-06-05 06:29:00.000000000 +0400
+++ /var/www/html/forum/Sources/Admin.php 2012-02-02 13:09:41.400143124 +0400
@@ -129,6 +129,7 @@
'subsections' => array(
'basic' => array($txt['mods_cat_features']),
'layout' => array($txt['mods_cat_layout']),
+ 'ldapauth' => array($txt['mods_cat_ldapauth']),
'karma' => array($txt['karma'], 'enabled' => in_array('k', $context['admin_features'])),
'sig' => array($txt['signature_settings_short']),
'profile' => array($txt['custom_profile_shorttitle'], 'enabled' => in_array('cp', $context['admin_features'])),
diff -u -r /home/user/temp/smf-2.0.2/Sources/LogInOut.php /var/www/html/forum/Sources/LogInOut.php
--- /home/user/temp/smf-2.0.2/Sources/LogInOut.php 2011-06-05 06:29:00.000000000 +0400
+++ /var/www/html/forum/Sources/LogInOut.php 2012-02-02 13:07:25.237979347 +0400
@@ -207,6 +204,17 @@
}

// Are we using any sort of integration to validate the login?
+
+ // Call smf_ldap_auth() or smf_kerb_auth() to prepopulate table. Fallback on LDAP authentication if we enter login from form.
+ if ($modSettings['ldapauth_enable'] && $modSettings['kerbauth_enable'] && isset($_SERVER['REMOTE_USER'])) {
+ require_once($sourcedir . '/KerbAuth.php');
+ $ext_auth = smf_kerb_auth($_POST['user'], $_POST['passwrd'], $modSettings['cookieTime']);
+ }
+ else {
+ require_once($sourcedir . '/LdapAuth.php');
+ $ext_auth = smf_ldap_auth($_POST['user'], $_POST['passwrd'], $modSettings['cookieTime']);
+ }
+
if (in_array('retry', call_integration_hook('integrate_validate_login', array($_POST['user'], isset($_POST['hash_passwrd']) && strlen($_POST['hash_passwrd']) == 40 ? $_POST['hash_passwrd'] : null, $modSettings['cookieTime'])), true))
{
$context['login_errors'] = array($txt['login_hash_error']);
@@ -289,7 +297,7 @@
$sha_passwd = sha1(strtolower($user_settings['member_name']) . un_htmlspecialchars($_POST['passwrd']));

// Bad password!  Thought you could fool the database?!
- if ($user_settings['passwd'] != $sha_passwd)
+ if ($user_settings['passwd'] != $sha_passwd && !$ext_auth)
{
// Let's be cautious, no hacking please. thanx.
validatePasswordFlood($user_settings['id_member'], $user_settings['passwd_flood']);
diff -u -r /home/user/temp/smf-2.0.2/Sources/ManageSettings.php /var/www/html/forum/Sources/ManageSettings.php
--- /home/user/temp/smf-2.0.2/Sources/ManageSettings.php 2011-06-05 06:29:00.000000000 +0400
+++ /var/www/html/forum/Sources/ManageSettings.php 2012-02-02 13:10:55.301317701 +0400
@@ -97,6 +97,7 @@

$subActions = array(
'basic' => 'ModifyBasicSettings',
+ 'ldapauth' => 'ModifyLdapAuthSettings',
'layout' => 'ModifyLayoutSettings',
'karma' => 'ModifyKarmaSettings',
'sig' => 'ModifySignatureSettings',
@@ -114,6 +115,8 @@
'tabs' => array(
'basic' => array(
),
+ 'ldapauth' => array(
+ ),
'layout' => array(
),
'karma' => array(
@@ -2056,4 +2058,51 @@
prepareDBSettingContext($config_vars);
}

-?>
\ В конце файла нет новой строки
+
+// Function to load template/modify Ldap Authentication settings
+function ModifyLdapAuthSettings($return_config = false)
+{
+ global $context, $txt, $scripturl, $settings, $modSettings, $db_prefix, $helptxt;
+
+ $config_vars = array(
+ array('check', 'ldapauth_enable'),
+// array('check', 'kerbauth_enable'),
+ array('text', 'ldapauth_serverurl'),
+ array('text', 'ldapauth_searchdn', '40'),
+ array('text', 'ldapauth_searchkey'),
+ array('text', 'ldapauth_uidattr'),
+ array('check', 'ldapauth_authresnames'),
+ array('check', 'ldapauth_regresnames'),
+ array('text', 'ldapauth_fullnameattr'),
+ array('text', 'ldapauth_uidattr'),
+ array('check', 'ldapauth_emailuselogin'),
+ array('text', 'ldapauth_emailsuffix'),
+ array('text', 'ldapauth_emailattr'),
+ array('check', 'ldapauth_locationuseou'),
+ array('text', 'ldapauth_locationattr'),
+ array('check', 'ldapauth_updateonlogin'),
+ array('check', 'ldapauth_passwdindb'),
+ array('text', 'ldapauth_bindusername'),
+ array('password', 'ldapauth_bindpassword')
+ );
+
+ if ($return_config)
+ return $config_vars;
+
+ // Saving?
+ if (isset($_GET['save']))
+ {
+ checkSession();
+ saveDBSettings($config_vars);
+ writeLog();
+
+ redirectexit('action=admin;area=featuresettings;sa=ldapauth');
+ }
+
+ $context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=ldapauth';
+ $context['settings_title'] = $txt['ldapauth_Title'];
+
+ prepareDBSettingContext($config_vars);
+
+}
+?>
diff -u -r /home/user/temp/smf-2.0.2/Sources/Profile-Modify.php /var/www/html/forum/Sources/Profile-Modify.php
--- /home/user/temp/smf-2.0.2/Sources/Profile-Modify.php 2011-11-30 04:44:21.000000000 +0400
+++ /var/www/html/forum/Sources/Profile-Modify.php 2012-01-24 11:43:00.085606711 +0400
@@ -273,7 +273,7 @@
'),
),
'email_address' => array(
- 'type' => 'text',
+ 'type' => (isset($modSettings['ldapauth_enable']) || $modSettings['ldapauth_enable']) && !allowedTo('admin_forum') ? 'label' : 'text',
'label' => $txt['email'],
'subtext' => $txt['valid_email'],
'log_change' => true,
@@ -427,7 +427,7 @@
'),
),
'location' => array(
- 'type' => 'text',
+ 'type' => (isset($modSettings['ldapauth_enable']) || $modSettings['ldapauth_enable']) && !allowedTo('admin_forum') ? 'label' : 'text',
'label' => $txt['location'],
'log_change' => true,
'size' => 50,
@@ -489,7 +489,7 @@
'subtext' => $txt['password_strength'],
'size' => 20,
'value' => '',
- 'enabled' => empty($cur_profile['openid_uri']),
+ 'enabled' => empty($cur_profile['openid_uri']) && !((isset($modSettings['ldapauth_enable']) || $modSettings['ldapauth_enable']) && !allowedTo('admin_forum')),
'permission' => 'profile_identity',
'save_key' => 'passwd',
// Note this will only work if passwrd2 also exists!
@@ -520,7 +520,7 @@
'passwrd2' => array(
'type' => 'password',
'label' => $txt['verify_pass'],
- 'enabled' => empty($cur_profile['openid_uri']),
+ 'enabled' => empty($cur_profile['openid_uri']) && !((isset($modSettings['ldapauth_enable']) || $modSettings['ldapauth_enable']) && !allowedTo('admin_forum')),
'size' => 20,
'value' => '',
'permission' => 'profile_identity',
@@ -600,6 +600,7 @@
'secret_question' => array(
'type' => 'text',
'label' => $txt['secret_question'],
+ 'enabled' => !((isset($modSettings['ldapauth_enable']) || $modSettings['ldapauth_enable']) && !allowedTo('admin_forum')),
'subtext' => $txt['secret_desc'],
'size' => 50,
'permission' => 'profile_identity',
@@ -607,6 +608,7 @@
'secret_answer' => array(
'type' => 'text',
'label' => $txt['secret_answer'],
+ 'enabled' => !((isset($modSettings['ldapauth_enable']) || $modSettings['ldapauth_enable']) && !allowedTo('admin_forum')),
'subtext' => $txt['secret_desc2'],
'size' => 20,
'postinput' => '<span class="smalltext" style="margin-left: 4ex;"><a href="' . $scripturl . '?action=helpadmin;help=secret_why_blank" onclick="return reqWin(this.href);">' . $txt['secret_why_blank'] . '</a></span>',
diff -u -r /home/user/temp/smf-2.0.2/Sources/Profile.php /var/www/html/forum/Sources/Profile.php
--- /home/user/temp/smf-2.0.2/Sources/Profile.php 2011-06-05 06:29:00.000000000 +0400
+++ /var/www/html/forum/Sources/Profile.php 2012-01-24 12:48:20.495600311 +0400
@@ -164,7 +164,7 @@
'function' => 'account',
'enabled' => $context['user']['is_admin'] || ($cur_profile['id_group'] != 1 && !in_array(1, explode(',', $cur_profile['additional_groups']))),
'sc' => 'post',
- 'password' => true,
+ 'password' => !(isset($modSettings['ldapauth_enable']) || $modSettings['ldapauth_enable']),
'permission' => array(
'own' => array('profile_identity_any', 'profile_identity_own', 'manage_membergroups'),
'any' => array('profile_identity_any', 'manage_membergroups'),
@@ -469,7 +469,7 @@

// All the subactions that require a user password in order to validate.
$check_password = $context['user']['is_owner'] && in_array($profile_include_data['current_area'], $context['password_areas']);
- $context['require_password'] = $check_password && empty($user_settings['openid_uri']);
+ $context['require_password'] = $check_password && (empty($user_settings['openid_uri'])  && !((isset($modSettings['ldapauth_enable']) || $modSettings['ldapauth_enable']) && !allowedTo('admin_forum')));

// If we're in wireless then we have a cut down template...
if (WIRELESS && $context['sub_template'] == 'summary' && WIRELESS_PROTOCOL != 'wap')

ops2012

Теперь bad news. Не было времени, совершенно, чтобы разбираться с кастомными полями в профиле. Поэтому пришлось поломать таблицу smf_members - для добавления поля UID. Поле текстовое ldapattr_uid. Если кто поможет переделать, так чтобы хранилось в дополнительном поле профиля, но так чтобы никто не мог это поле поправить - тому моё спасибо.
Очень хочется использовать возможность выдёргивать любую информацию из ldap и кидать её в нужное поле профиля. С ходу не нашёл это место. Но идея простая. Если установлена LDAP аутентификация, то показывать дополнительное текстовое поле. В нём можно будет выставить ldap атрибуты. Если поле непустое, то ldap запрос вытаскивает инфу с сервера ldap и кидает в базу и поле запрещено для правки.
Сейчас кину керберос часть. И конечно там опять дублирование функций идёт. Времени на вынос в функции пока нет.

ops2012

Файл KerbAuth.php - кидать опять же в каталог Source.
<?php

// kerbauth_enable -- will prevent anything from being done if not set
// ldapauth_enable -- will prevent anything from being done if not set
// ldapauth_serverurl -- 'ldap://yourldapserver';
// ldapauth_usersuffix -- with MSAD will be set to '@yourdomain.yourtld';
// ldapauth_userprefix -- useful in conjunction with ldapauth_usersuffix to create a dn-based login
// ldapauth_searchdn -- 'OU=Your Users,DC=yourdomain,DC=yourtld';
// ldapauth_searchKey -- for MSAD will be 'sAMAccountName';
// ldapauth_emailuselogin -- set to indicate that ldapauth_emailsuffix should be used 
// ldapauth_emailsuffix -- added to the login username to create the users' email addresses
// ldapauth_emailattr -- if not using ldapauth_emailsuffix, the attribute in ldap to be used for the email addresses
// ldapauth_locationuseou -- use the top level ou as the users' locations
// ldapauth_locationattr -- if not using ldapauth_locationuseou, the attribute in ldap to be used for the locations
// ldapauth_updateonlogin -- set to indicate that the user's information should be updated on every login
// ldapauth_fullnameattr -- the attribute in ldap to be used for the fullname (cn for MSAD); required
// ldapauth_regresnames -- set to indicate that ldap auth should autoregister/update users with reserved name logins
// ldapauth_authresnames -- set to indicate that ldap auth should authenticate users with reserved name logins
// ldapauth_passwdindb -- set to indicate that ldap passwords should be stored in the local database
// ldapauth_uidattr -- the attribute in ldap to be used for the UID (objectsid for MSAD)

//TODO much error handling, especially where connections and queries fail, or the user has bad or missing settings

function smf_kerb_auth($username$password$seconds)
{
global $db_prefix$user_info$modSettings$func$txt$sourcedir$smcFunc;
require_once($sourcedir '/Subs-Members.php');
if (!isset($modSettings['ldapauth_enable']) || !$modSettings['ldapauth_enable'] || !isset($modSettings['kerbauth_enable']) || !$modSettings['kerbauth_enable'])
return false;
if ((!isset($modSettings['ldapauth_authresnames']) || !$modSettings['ldapauth_authresnames']) && isReservedName($username))
return false;
if (!isset($_SERVER['REMOTE_USER']))
{
log_error("apache don't setup for kerberos");
return false;
}
// Set kerberos username and REALM from apache. kick REALM
// list($kerbuser, $realm) = explode("@", strtolower($_SERVER['REMOTE_USER']));

// if ($kerbuser !== $username) {
// $user_info['is_guest']=true;
// return false;
// }
// We can use the password passed in since we abandoned the integration hack
$thepasswrd $password;
$lattributes explode(','$modSettings['ldapauth_fullnameattr']);
if (isset($modSettings['ldapauth_emailattr']) && !empty($modSettings['ldapauth_emailattr']))
$lattributes[] = $modSettings['ldapauth_emailattr'];
if (isset($modSettings['ldapauth_locationattr']) && !empty($modSettings['ldapauth_locationattr']))
$lattributes[] = $modSettings['ldapauth_locationattr'];
if (isset($modSettings['ldapauth_uidattr']) && !empty($modSettings['ldapauth_uidattr']))
$lattributes[] = $modSettings['ldapauth_uidattr'];

$sha_passwd sha1(strtolower($username) . un_htmlspecialchars(stripslashes($thepasswrd)));
if ($lds ldap_connect($modSettings['ldapauth_serverurl']))
{
// these next two are required for recent versions of MSAD, 
// but may need tweak options for other ldap servers
ldap_set_option($ldsLDAP_OPT_PROTOCOL_VERSION3);
ldap_set_option($ldsLDAP_OPT_REFERRALS0);


// if using an application account to pre-bind, find the user's DN with the pre-bind account
$userDN "";
if (isset($modSettings['ldapauth_bindusername']) && $modSettings['ldapauth_bindusername'])
{
if ($bd ldap_bind($lds$modSettings['ldapauth_bindusername'], $modSettings['ldapauth_bindpassword']))
{
log_error("Bound with " $modSettings['ldapauth_bindusername']);
$results=ldap_search($lds$modSettings['ldapauth_searchdn'], "(|({$modSettings['ldapauth_searchkey']}=$username))");
$lentries ldap_get_entries($lds$results);
$found $lentries['count'];
if(!$found// if the user was not found in LDAP
{
log_error("Username $username not found in LDAP.");
ldap_unbind($lds);
return false;
}
log_error("Username $username found in LDAP. Continuing...");
$userDN $lentries[0]['dn'];
}
}
if(!$userDN$userDN $modSettings['ldapauth_userprefix'] . $username $modSettings['ldapauth_usersuffix'];
//clear passwd if we're not going to store it in the db
if (isset($modSettings['ldapauth_passwdindb']) && !$modSettings['ldapauth_passwdindb'])
$sha_passwd "LDAPOnly";

log_error('Bound with '.$username);
$lentries ldap_get_entries($ldsldap_search($lds$modSettings['ldapauth_searchdn'], "({$modSettings['ldapauth_searchkey']}=$username)"$lattributes));
//verify user exests by ldap uid and update memeber_name if changed
if (isset($modSettings['ldapauth_uidattr']) && !empty($modSettings['ldapauth_uidattr']))
{
$luid bin_to_str_sid($lentries[0][$modSettings['ldapauth_uidattr']][0]);
$request $smcFunc['db_query']('''
SELECT id_member
FROM {db_prefix}members
WHERE ' 
'ldapattr_uid = {string:lid_member}' '
LIMIT 1'
,
array(
'lid_member' => $luid,
)
);
if (($smcFunc['db_num_rows']($request) > 0) && !strcmp($username$cresult['member_name']))
{
$cresult $smcFunc['db_fetch_assoc']($request);
updateMemberData($cresult['id_member'], array('member_name' => $username));
}
$smcFunc['db_free_result']($request);
}
//verify user exists in forum user database
$request $smcFunc['db_query']('''
SELECT id_member
FROM {db_prefix}members
WHERE ' 
. ($smcFunc['db_case_sensitive'] ? 'LOWER(member_name) = LOWER({string:user_name})' 'member_name = {string:user_name}') . '
LIMIT 1'
,
array(
'user_name' => $smcFunc['db_case_sensitive'] ? strtolower($username) : $username,
)
);
//if this isn't set we won't authenticate a reserved name that isn't already registered
if ((!isset($modSettings['ldapauth_regresnames']) || !$modSettings['ldapauth_regresnames']) && isReservedName($username) && $smcFunc['db_num_rows']($request) == 0)
return false;

//Hairy!
// IF 
// (
//  the user isn't already registered 
//  OR 
// (
// updateonlogin is set 
// AND 
// updateonlogin is true
// )
// )
// AND 
// (
// (
// regresnames is set 
// AND 
// regresname is true
//
// OR 
// the username is NOT reserved
// )
if (($smcFunc['db_num_rows']($request) == || (isset($modSettings['ldapauth_updateonlogin']) && $modSettings['ldapauth_updateonlogin'])) && ((isset($modSettings['ldapauth_regresnames']) && $modSettings['ldapauth_regresnames']) || !isReservedName($username)))
{
// We're going to update or add the user, so setup the data we need

// Get user's full name (and possibly email address & location) from the directory
// $lentries = ldap_get_entries($lds, ldap_search($lds, $modSettings['ldapauth_searchdn'], "({$modSettings['ldapauth_searchkey']}=$username)", $lattributes));

if (isset($modSettings['ldapauth_locationuseou']) && $modSettings['ldapauth_locationuseou'])
{
// Parse the highest level Organizational Unit as their location
$i1 strrpos($lentries[0]["dn"],'OU=')+3;
$i2 strpos($lentries[0]["dn"],',DC=',$i1);
$llocation substr($lentries[0]["dn"],$i1,$i2-$i1);
}

else if (isset($modSettings['ldapauth_locationattr']) && !empty($modSettings['ldapauth_locationattr'])) {
$llocation '';
$ldapauth_loc explode(','$modSettings['ldapauth_locationattr']);
foreach ($ldapauth_loc as $value) {
$llocation .= $lentries[0][$value][0] . ' ';
}
$llocation chop ($llocation);
 }
else $llocation '';
if (isset($modSettings['ldapauth_emailuselogin']) && $modSettings['ldapauth_emailuselogin']) 
$lmail $username . (isset($modSettings['ldapauth_emailsuffix']) ? $modSettings['ldapauth_emailsuffix'] : '');
else if (isset($modSettings['ldapauth_emailattr']) && !empty($modSettings['ldapauth_emailattr']))
$lmail $lentries[0][$modSettings['ldapauth_emailattr']][0];
else $lmail '';
//email checking courtesy of Subs-Members.php
if (empty($lmail) || preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~'stripslashes($lmail)) === || strlen(stripslashes($lmail)) > 255)
                
{
log_error(sprintf($txt['valid_email_needed'], $username));
$lmail ''; // we will let this go, although SMF doesn't really like missing email addresses
}
$thelocation $smcFunc['htmlspecialchars']($llocation);
$lrealname '';
$ldapauth_rname explode(','$modSettings['ldapauth_fullnameattr']);
foreach ($ldapauth_rname as $value) {
$lrealname .= $lentries[0][$value][0] .' ';
}
$lrealname chop ($lrealname);
$therealname $smcFunc['htmlspecialchars']($lrealname);

if ($smcFunc['db_num_rows']($request) > 0)
{
// User actually exists, so only update ldap-changeable data
$cresult $smcFunc['db_fetch_assoc']($request);
updateMemberData($cresult['id_member'], array('passwd' => $sha_passwd'email_address' => $lmail,'real_name' => $therealname'location' => $thelocation));
ldap_close($lds);
$smcFunc['db_free_result']($request);
// tells LogInOut.php not to check passwd, since we already found it to be good
return true;
} else {
log_error('Not Bound with user ' $username);
}

// User does not exist in SMF database - create
$regOptions = array(
'interface' => 'guest',
'username' => $username,
'email' => $lmail,
'password' => $sha_passwd,
'require' => 'nothing',
'password_check' => $sha_passwd,
'validation_code' => "''",
'check_reserved_name' => true,
'check_password_strength' => false,
'check_email_ban' => true,
'send_welcome_email' => !empty($modSettings['send_welcomeEmail']),
);
$memberID registerMember($regOptions);
updateMemberData($memberID, array('location' => $thelocation'real_name' => $therealname'passwd' => $sha_passwd));
if (isset($modSettings['ldapauth_uidattr']) && !empty($modSettings['ldapauth_uidattr']))
{
$luid bin_to_str_sid($lentries[0][$modSettings['ldapauth_uidattr']][0]);
updateMemberData($memberID, array('ldapattr_uid' => $luid));
}
updateStats('member');

// If it's enabled, increase the registrations for today.
trackStats(array('registers' => '+'));

// Actually, this is the entry, not the results, which are anonymous.  
// Oh well, it'll be freed once the page is served anyway.
//ldap_free_result($lentries);
}
else
if (!isset($modSettings['ldapauth_passwdindb']) || $modSettings['ldapauth_passwdindb'])
{
// User does exist (or it's reserved and we've set the option not to update data for reserved names, 
// but we'll update the password in case it's changed
$cresult $smcFunc['db_fetch_assoc']($result);
updateMemberData($cresult['id_member'], array('passwd' => $sha_passwd));
}
ldap_close($lds);
$smcFunc['db_free_result']($request);
// tells LogInOut.php not to check passwd, since we already found it to be good
return true;
}
ldap_close($lds);

// allow authentication fallthrough to other methods (local database hashes of various kinds, by default)
return false;
}

// Returns the textual SID
function bin_to_str_sid($binsid) {
    
$hex_sid bin2hex($binsid);
    
$rev hexdec(substr($hex_sid02));
    
$subcount hexdec(substr($hex_sid22));
    
$auth hexdec(substr($hex_sid412));
    
$result    "$rev-$auth";

    for (
$x=0;$x $subcount$x++) {
        
$subauth[$x] = 
            
hexdec(little_endian(substr($hex_sid16 + ($x 8), 8)));
        
$result .= "-" $subauth[$x];
    }

    
// Cheat by tacking on the S-
    
return 'S-' $result;
}

// Converts a little-endian hex-number to one, that 'hexdec' can convert
function little_endian($hex) {
    for (
$x strlen($hex) - 2$x >= 0$x $x 2) {
        
$result .= substr($hex$x2);
    }
    return 
$result;
}

?>


ops2012

Теперь собственно настройка апаче. Там всё просто - главное чтобы система была кирберезована и получила тикет  :)
В апаче должен загружаться модуль поддержки кербероса. В конфиге вот это:
<Directory "/var/www/html/forum">
    # Kerberos Auth
    AuthType Kerberos
    AuthName "Kerberos Login"
    KrbAuthRealms AD.COM
    KrbVerifyKDC off
    KrbServiceName HTTP
    Krb5Keytab /etc/krb5.keytab
    KrbMethodNegotiate on
    KrbMethodK5Passwd on
    Require valid-user
</Directory>


Строчка  "KrbMethodK5Passwd on" нужна для запроса имени-пароля если компьютер пользователя не керберезован. Да те же планшетки, либо те же компьютеры не введённые в MSAD. И тут первая проблема - гостю нельзя получить доступ на форум. Керберос его обламывает. Убирание строчки "Require valid-user" частично помогло - позволило зайти гостем. Но начались проблемы автоматического входа. Либо не авторизуется автоматом, либо после разлогина не хочет автоматически авторизоваться.
В итоге я от гостевого входа отказался, пока. Попозже ещё в эту сторону покапаю.
И тут я словил вторую проблему. Через некоторое время нахождения на форуме страничка, при перезагруке, нормально всё отображала. Но пыталась ещё что-то докачать. И этот процесс остановить было нельзя. Хотя по ссылкам ходит, всё показывает. Поковыряв через firebug похоже нашёл причину. Практически всегда это были скрипты sript.js theme.js и т.д. Иногда проскакивают картинки из темы. Судя по всему javascript один раз тыкается за каким-нибудь элементом и почему-то висит в ожидании. Как это отследить и исправить - до сих пор ума не приложу.

vector

Всем привет. Подниму некротемку.  crazy  По сабжу решили путем установки мода отсюда https://github.com/ppschweiz/smf-ldapauth Правда, пришлось допилить пару скриптов под Postgresql, по ссылке похоже под MySQL сделано.

ispanec

vector, каким образом устанавливается данная модификация?
И как данная модификация дружит(или не дружит) с деревом?

P.S. сильно не пинайте, только установил форум, а в readme нет никакой информации.