getValue( 'SELECT sync FROM account.user WHERE name = #', [$userName] ); if ($isSync) return; self::sync($db, $userName, $password); } static function sync($db, $userName, $password = NULL, $force = TRUE) { $hasAccount = $db->getValue( 'SELECT COUNT(*) > 0 FROM account.user u JOIN account.account a ON u.id = a.id WHERE u.name = #', [$userName] ); if ($hasAccount) { self::ldapSync($db, $userName, $password); self::sambaSync($db, $userName, $password); } $bcryptPassword = password_hash($password, PASSWORD_BCRYPT); $userId = $db->getValue( 'SELECT id FROM account.user WHERE `name` = #', [$userName] ); $db->query( 'CALL account.user_syncPassword(#, #)', [$userId, $password] ); $db->query( 'UPDATE account.user SET sync = TRUE, bcryptPassword = # WHERE id = #', [$bcryptPassword, $userId] ); } /** * Synchronizes the user credentials in the LDAP server. */ static function ldapSync($db, $userName, $password) { // Gets LDAP configuration parameters $conf = $db->getObject( 'SELECT host, rdn, password, baseDn, filter FROM account.ldapConfig'); // Connects an authenticates against server $ds = ldap_connect($conf->host); if (!$ds) throw new Exception("Can't connect to LDAP server: ". ldapError($ds)); try { ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3); $bind = ldap_bind($ds, $conf->rdn, base64_decode($conf->password)); if (!$bind) throw new Exception("Authentication failed on LDAP server: ". ldapError($ds)); // Prepares the data $domain = $db->getValue('SELECT domain FROM account.mailConfig'); $user = $db->getObject( 'SELECT id, nickname, lang FROM account.user WHERE name = #', [$userName] ); $cn = empty($user->nickname) ? $userName : $user->nickname; $nameArgs = explode(' ', $user->nickname); $givenName = $nameArgs[0]; if (count($nameArgs) > 1) $sn = $nameArgs[1]; if (empty($sn)) $sn = 'Empty'; $attrs = [ 'cn' => $cn, 'displayName' => $user->nickname, 'givenName' => $givenName, 'sn' => $sn, 'mail' => "$userName@{$domain}", 'userPassword' => sshaEncode($password), 'preferredLanguage' => $user->lang ]; // Search the user entry $filter = "uid=$userName"; if (!empty($conf->filter)) $filter = "(&($filter)($conf->filter))"; $res = ldap_search($ds, $conf->baseDn, $filter); if (!$res) throw new Exception("Can't get the LDAP entry: ". ldapError($ds)); $dn = "uid=$userName,{$conf->baseDn}"; $entry = ldap_first_entry($ds, $res); $classes = ldap_get_values($ds, $entry, 'objectClass'); if (!in_array('inetOrgPerson', $classes)) { ldap_delete($ds, $dn); $entry = NULL; } if ($entry) { $modifs = []; $curAttrs = ldap_get_attributes($ds, $entry); foreach ($attrs as $attribute => $value) if (!empty($value)) { $modifs[] = [ 'attrib' => $attribute, 'modtype' => LDAP_MODIFY_BATCH_REPLACE, 'values' => [$value] ]; } elseif (isset($curAttrs[$attribute])) { $modifs[] = [ 'attrib' => $attribute, 'modtype' => LDAP_MODIFY_BATCH_REMOVE_ALL ]; } $updated = ldap_modify_batch($ds, $dn, $modifs); } else { $addAttrs = []; foreach ($attrs as $attribute => $value) if (!empty($value)) $addAttrs[$attribute] = $value; $addAttrs = array_merge($addAttrs, [ 'objectClass' => ['inetOrgPerson'], 'uid' => $userName ]); $updated = ldap_add($ds, $dn, $addAttrs); } if (!$updated) throw new Exception("Can't update the LDAP entry: ". ldapError($ds)); } catch (Exception $e) { ldap_unbind($ds); throw $e; } } /** * Synchronizes the user credentials in the Samba server. */ static function sambaSync($db, $userName, $password) { $conf = $db->getObject( 'SELECT host, sshUser, sshPass, uidBase FROM account.sambaConfig' ); $domain = $db->getValue('SELECT domain FROM account.mailConfig'); $samba = new SshConnection($conf->host ,$conf->sshUser ,base64_decode($conf->sshPass) ); $scriptDir = '/mnt/storage/scripts'; // Creates the Samba user and initializes it's home directory $userId = $db->getValue( 'SELECT id FROM account.user WHERE name = #', [$userName]); $samba->exec("$scriptDir/create-user.sh %s %s %s" ,$userName ,$conf->uidBase + $userId ,"$userName@{$domain}" ); // Syncronizes the Samba password if (empty($password)) return; $samba->exec("$scriptDir/set-password.sh %s %s" ,$userName ,$password ); } } function ldapError($ds) { return ldap_errno($ds) .': '. ldap_error($ds); } function sshaEncode($value) { mt_srand((double) microtime() * 1000000); $salt = pack('CCCC', mt_rand(), mt_rand(), mt_rand(), mt_rand()); $hash = '{SSHA}' . base64_encode(pack('H*', sha1($value . $salt)) . $salt); return $hash; } function sshaVerify($hash, $value) { $ohash = base64_decode(substr($hash, 6)); $osalt = substr($ohash, 20); $ohash = substr($ohash, 0, 20); $nhash = pack('H*', sha1($value . $osalt)); return $ohash == $nhash; } class SshConnection { var $connection; /** * Abrebiated method to make SSH connections. */ function __construct($host, $user, $password) { $this->connection = $connection = ssh2_connect($host); if (!$connection) throw new Exception("Can't connect to SSH server $host"); $authOk = ssh2_auth_password($connection, $user, $password); if (!$authOk) throw new Exception("SSH authentication failed on server $host"); return $connection; } /** * Executes a command on the host. */ function exec() { $nargs = func_num_args(); $args = func_get_args(); for ($i = 1; $i < $nargs; $i++) $args[$i] = self::escape($args[$i]); $command = call_user_func_array('sprintf', $args); return ssh2_exec($this->connection, $command); } /** * Escapes the double quotes from an string. */ static function escape($str) { return '"'. str_replace('"', '\\"', $str) .'"'; } }