konstantin_18
Guest
Класс работы с .htpass файлом
Я недаавно встретился с проблемой: надо было сделать аутентификацию для 10 юзеров в отдельный каталог. Я сделал через .haccess, всем понравилось, но получился БОЛЬШОЙ облом: хостер не предоставлял доступа к утилите htpasswd чтобы пароли юзеров менять или добавлять кого-то. Кроме этого даже если бы такое предоставлялось, то эта утилита не умеет удалять юзеров из файла. Это надо было делать ручками.
Я написал класс. Извините, работаю не на наших, поэтому все comments по английски. Если многим надо будет, я переведу, но... пока облом, честно говоря.
Короче, вот класс:
Ниже простенький менеджер, который использует этот класс в своей работе. Не используется только сортировка:
Поскольку я не могу назвать себя крутым профи, принимаютсявсе комментарии, критика и т.д. Единственная просьба - обосновано и культурно
))
Хочу чтобы оценили как конкретный код так и правильность подхода к решению этой задачи.
Всем спасибо.
-~{}~ 22.09.05 22:28:
PS. Просьба при использовании этого класса в своем коде поставить ссылочку (в комментариях конечно) на меня. И указать сайтик x-planet.ru
-~{}~ 22.09.05 22:30:
PPS. Тема создана после обсуждения:
http://phpclub.ru/talk/showthread.php?s=&threadid=73297&rand=10
Я недаавно встретился с проблемой: надо было сделать аутентификацию для 10 юзеров в отдельный каталог. Я сделал через .haccess, всем понравилось, но получился БОЛЬШОЙ облом: хостер не предоставлял доступа к утилите htpasswd чтобы пароли юзеров менять или добавлять кого-то. Кроме этого даже если бы такое предоставлялось, то эта утилита не умеет удалять юзеров из файла. Это надо было делать ручками.
Я написал класс. Извините, работаю не на наших, поэтому все comments по английски. Если многим надо будет, я переведу, но... пока облом, честно говоря.
Короче, вот класс:
PHP:
<?php
/**
*@author Konstantin Mirin <[email protected]><[email protected]>
*@deprec This class allows to work with .htpass file like htpasswd command does. I wrote
*it because some hosters do not provide access to this command (In my practice I had several
*ones).
*And at the same time, apache authorisation is very convenient if you want to grant
*access to some part of the site only for several persons and using of database is not
*needed (or allowed)
*/
/**
*Class allows to work with apache user's file easily
*/
class ApacheAuth
{
/**
*file with passwords
*/
var $_file;
/**
*users array
*/
var $_users;
/**
*array of user's logins
*/
var $_logins;
/**
*number of users
*/
var $_usersnum;
/**
*Constructor of the class. It sets path to the .passwd file and reads
*the file into array
*/
function ApacheAuth($path = null)
{
$this->_file = $path;
$this->_ReadPasswordFile();
}
/**
*function reads the password file to the array
*/
function _ReadPasswordFile()
{
if (file_exists($this->_file))//if there is any file
{
$users = file_get_contents($this->_file);
//it takes it's contents
$users = explode("\n", $users);
//explode()s it by new line symbol
$this->_usersnum = sizeof($users)-1;
//calcualtes the number of linew with users
for ($i = 0; $i < $this->_usersnum; $i++)
//cycles throught them
{
$user = explode(':', $users[$i]);
//divides each line into login and crypted password
$this->_users[$i] = array(
'login' => $user[0],//ligin
'pass' => substr($user[1],2),//password itself
'salt' => substr($user[1],0,2));//salt for password
//writes all data to the users array
$this->_logins[$i] = $user[0];
//and wrutes login to the logins array.
//It serves for quick search of the user
}
}
else
{
//if there is no any file, we set variables to avoid
//any possible errors due to wrong data type
$this->_logins = array();
$this->_users = array();
$this->_usersnum = 0;
}
}
/**
*function returns true if there is any user with such login and false
*if there is none
*/
function IsRegistered($login)
{
if (in_array($login, $this->_logins)) return true;
return false;
}
/**
*function simply creates random password of the defined length
*/
function _CreateRandomPassword($n)
{
$chars = "abcdefghijkmnopqrstuvwxyz023456789";
srand((double)microtime()*1000000);
$i = 0;
$pass = '' ;
while ($i <= $n-1)
{
$num = rand() % 33;
$tmp = substr($chars, $num, 1);
$pass = $pass . $tmp;
$i++;
}
return $pass;
}
/**
*function encryptes password using standart UNIX DES algorythm,
* which is standart for htpasswd utility
*@param string $pass It is password than needs to be encrypted
*@return string $salt it is the salt with which pass war encrypted
*@return string $pass it is the password without salt
*Variables are returned in the form of the assisiative array
*/
function _CryptPass($pass)
{
$salt = $this->_CreateRandomPassword(2);
$pass = substr(crypt($pass, $salt),2);
return array('salt'=>$salt,'pass'=>$pass);
}
/**
*Function checks, if the password is valid.
*@param integer $id It is the index of user in users array
*@param string $pass It is password that needs to be checked
*@return boolean $result It is the result of check
*/
function _CheckPass($id, $pass)
{
$oldpass = $this->_users[$id]['pass'];
$salt = $this->_users[$id]['salt'];
echo 'CRYPT()ED PASS'.crypt($pass, $salt).'<br>';
if ($salt.$oldpass === crypt($pass, $salt)) return true;
return false;
}
/**
*Function adds user to the file if this user is not there currently
*/
function AddUser($login, $pass)
{
if (!$this->IsRegistered($login))//we check, if it is registered
{
$pass = $this->_CryptPass($pass);//we crypt() password
$user = array(
'login' => $login,
'pass' => $pass['pass'],
'salt' => $pass['salt']
);
array_push($this->_users, $user);
//and write appropriate data to the array of users
array_push($this->_logins, $login);
//and array of logins
$this->_usersnum +=1;
//and increase number of users in the list
}
else return false;
return true;
}
/**
*Function finds user
*@param string $login This is login of user to be found
*@return integer $result This is the index for the element in
* the users array containing data about this user
*/
function _FindUser($login)
{
return array_search($login, $this->_logins);
}
/**
*Function compares two users to define, who of them must be first.
*if uses strcmp function for comparison
*/
function _CMPUsers($a, $b)
{
return strcmp($a['login'], $b['login']);
}
/**
*function sorts array of users and array of logins
*/
function SortUsers()
{
usort($this->_users, array('ApacheAuth', '_CMPUsers'));
sort($this->_logins);
}
/**
*Funscions changes user's data
*@param string $login it is the login of the user
*@param string $oldpass it is the olda pass for the user
*@param string $newpass it is the new password for the user
*/
function ChangeUser($login, $oldpass, $newpass)
{
$id = $this->_FindUser($login);
//we find user in the array
if ($this->_CheckPass($id, $oldpass))
//if old password is valid....
//if you don't want this check to be performed,
//simply delete if condition and $oldapass parameter from function
{
$pass = $this->_CryptPass($newpass);
//function crypts password
$this->_users[$id]['salt'] = $pass['salt'];
$this->_users[$id]['pass'] = $pass['pass'];
//and we write it to the array
}
else return false;
return true;
}
/**
*Function deletes user
*@param string $login it is the login of the user that will be deleted
*/
function DeleteUser($login)
{
$id = $this->_FindUser($login);
unset($this->_users[$id]);
unset($this->_logins[$id]);
$this->_usersnum -=1;
}
/**
*Function commits all changes that were done to the user's list.
*it writes the users array to the .htpass file
*If doesn't write lines with empty user or password.
*/
function Commit()
{
$this->SortUsers();
for ($i = 0; $i < $this->_usersnum; $i++)
{
$user = $this->_users[$i];
if (empty($user['login'])||empty($user['pass'])) continue;
$usersfile .= $user['login'].':'.$user['salt'].$user['pass']."\n";
}
$fl = fopen($this->_file,'w+');
fwrite($fl, $usersfile);
fclose($fl);
unset($this->_logins);
unset($this->_users);
}
/**
* Function returns the list of users. It returns logins, not all array because all
* array is useless, passwords are crypted...
*/
function GetUsers()
{
return $this->_logins;
}
/**
* Function returns the list of users with theri all data.
*/
function GetAllUsersData()
{
return $this->_users;
}
/**
* Returns the number of users.
*/
function GetUsersNum()
{
return $this->_usersnum;
}
}
?>
PHP:
<?php
include('lib/apacheauth.inc.php');
$auth = new ApacheAuth('/home/kmirin/.htpass');
$todo = $_GET['todo'];
if (empty($todo)) $todo = $_POST['todo'];
switch ($todo)
{
case 'adduser':
{
$auth->AddUser($_POST['login'], $_POST['pass']);
$auth->Commit();
break;
}
case 'dispadd':
{
echo
'
<form action="manager.php" method="POST">
<input type="hidden" name="todo" value="adduser">
<input type="text" name="login"><br>
<input type="text" name="pass"><br>
<input type="submit">
</form>
';
break;
}
case 'changeuser':
{
$auth->ChangeUser($_POST['login'], $_POST['pass'], $_POST['newpass']);
$auth->Commit();
break;
}
case 'dispedit':
{
echo
'
<form action="manager.php" method="POST">
<input type="hidden" name="todo" value="changeuser">
<input type="text" name="login"><br>
<input type="text" name="pass"><br>
<input type="text" name="newpass"><br>
<input type="submit">
</form>
';
break;
}
case 'delete':
{
$auth->DeleteUser($_POST['login']);
$users = $auth->GetUsers();
$auth->Commit();
break;
}
default:
{
$users = $auth->GetUsers();
echo '<table border=1>';
for ($i = 0; $i < sizeof($users); $i++)
{
echo "
<tr>
<td>"
.$users[$i]['login'].
"</td>
<td>"
.$users[$i]['salt'].
"</td>
<td>"
.$users[$i]['pass'].
"</td>
<td>
<a href=\"manager.php?todo=dispedit\">edit</a><br>
<a href=\"manager.php?todo=dispadd\">add</a><br>
<a href=\"manager.php?todo=delete\">delete</a>
</td>
</tr>
";
}
echo '</table><br><a href="manager.php?todo=dispadd">add</a>';
}
}
?>

Хочу чтобы оценили как конкретный код так и правильность подхода к решению этой задачи.
Всем спасибо.
-~{}~ 22.09.05 22:28:
PS. Просьба при использовании этого класса в своем коде поставить ссылочку (в комментариях конечно) на меня. И указать сайтик x-planet.ru
-~{}~ 22.09.05 22:30:
PPS. Тема создана после обсуждения:
http://phpclub.ru/talk/showthread.php?s=&threadid=73297&rand=10