You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
6.6 KiB
263 lines
6.6 KiB
4 years ago
|
<?php
|
||
|
|
||
|
/*
|
||
|
|
||
|
Copyright (c) 2009-2019 F3::Factory/Bong Cosca, All rights reserved.
|
||
|
|
||
|
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||
|
|
||
|
This is free software: you can redistribute it and/or modify it under the
|
||
|
terms of the GNU General Public License as published by the Free Software
|
||
|
Foundation, either version 3 of the License, or later.
|
||
|
|
||
|
Fat-Free Framework is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License along
|
||
|
with Fat-Free Framework. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
*/
|
||
|
|
||
|
//! Authorization/authentication plug-in
|
||
|
class Auth {
|
||
|
|
||
|
//@{ Error messages
|
||
|
const
|
||
|
E_LDAP='LDAP connection failure',
|
||
|
E_SMTP='SMTP connection failure';
|
||
|
//@}
|
||
|
|
||
|
protected
|
||
|
//! Auth storage
|
||
|
$storage,
|
||
|
//! Mapper object
|
||
|
$mapper,
|
||
|
//! Storage options
|
||
|
$args,
|
||
|
//! Custom compare function
|
||
|
$func;
|
||
|
|
||
|
/**
|
||
|
* Jig storage handler
|
||
|
* @return bool
|
||
|
* @param $id string
|
||
|
* @param $pw string
|
||
|
* @param $realm string
|
||
|
**/
|
||
|
protected function _jig($id,$pw,$realm) {
|
||
|
$success = (bool)
|
||
|
call_user_func_array(
|
||
|
[$this->mapper,'load'],
|
||
|
[
|
||
|
array_merge(
|
||
|
[
|
||
|
'@'.$this->args['id'].'==?'.
|
||
|
($this->func?'':' AND @'.$this->args['pw'].'==?').
|
||
|
(isset($this->args['realm'])?
|
||
|
(' AND @'.$this->args['realm'].'==?'):''),
|
||
|
$id
|
||
|
],
|
||
|
($this->func?[]:[$pw]),
|
||
|
(isset($this->args['realm'])?[$realm]:[])
|
||
|
)
|
||
|
]
|
||
|
);
|
||
|
if ($success && $this->func)
|
||
|
$success = call_user_func($this->func,$pw,$this->mapper->get($this->args['pw']));
|
||
|
return $success;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* MongoDB storage handler
|
||
|
* @return bool
|
||
|
* @param $id string
|
||
|
* @param $pw string
|
||
|
* @param $realm string
|
||
|
**/
|
||
|
protected function _mongo($id,$pw,$realm) {
|
||
|
$success = (bool)
|
||
|
$this->mapper->load(
|
||
|
[$this->args['id']=>$id]+
|
||
|
($this->func?[]:[$this->args['pw']=>$pw])+
|
||
|
(isset($this->args['realm'])?
|
||
|
[$this->args['realm']=>$realm]:[])
|
||
|
);
|
||
|
if ($success && $this->func)
|
||
|
$success = call_user_func($this->func,$pw,$this->mapper->get($this->args['pw']));
|
||
|
return $success;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* SQL storage handler
|
||
|
* @return bool
|
||
|
* @param $id string
|
||
|
* @param $pw string
|
||
|
* @param $realm string
|
||
|
**/
|
||
|
protected function _sql($id,$pw,$realm) {
|
||
|
$success = (bool)
|
||
|
call_user_func_array(
|
||
|
[$this->mapper,'load'],
|
||
|
[
|
||
|
array_merge(
|
||
|
[
|
||
|
$this->args['id'].'=?'.
|
||
|
($this->func?'':' AND '.$this->args['pw'].'=?').
|
||
|
(isset($this->args['realm'])?
|
||
|
(' AND '.$this->args['realm'].'=?'):''),
|
||
|
$id
|
||
|
],
|
||
|
($this->func?[]:[$pw]),
|
||
|
(isset($this->args['realm'])?[$realm]:[])
|
||
|
)
|
||
|
]
|
||
|
);
|
||
|
if ($success && $this->func)
|
||
|
$success = call_user_func($this->func,$pw,$this->mapper->get($this->args['pw']));
|
||
|
return $success;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* LDAP storage handler
|
||
|
* @return bool
|
||
|
* @param $id string
|
||
|
* @param $pw string
|
||
|
**/
|
||
|
protected function _ldap($id,$pw) {
|
||
|
$port=(int)($this->args['port']?:389);
|
||
|
$filter=$this->args['filter']=$this->args['filter']?:"uid=".$id;
|
||
|
$this->args['attr']=$this->args['attr']?:["uid"];
|
||
|
array_walk($this->args['attr'],
|
||
|
function($attr)use(&$filter,$id) {
|
||
|
$filter=str_ireplace($attr."=*",$attr."=".$id,$filter);});
|
||
|
$dc=@ldap_connect($this->args['dc'],$port);
|
||
|
if ($dc &&
|
||
|
ldap_set_option($dc,LDAP_OPT_PROTOCOL_VERSION,3) &&
|
||
|
ldap_set_option($dc,LDAP_OPT_REFERRALS,0) &&
|
||
|
ldap_bind($dc,$this->args['rdn'],$this->args['pw']) &&
|
||
|
($result=ldap_search($dc,$this->args['base_dn'],
|
||
|
$filter,$this->args['attr'])) &&
|
||
|
ldap_count_entries($dc,$result) &&
|
||
|
($info=ldap_get_entries($dc,$result)) &&
|
||
|
$info['count']==1 &&
|
||
|
@ldap_bind($dc,$info[0]['dn'],$pw) &&
|
||
|
@ldap_close($dc)) {
|
||
|
return in_array($id,(array_map(function($value){return $value[0];},
|
||
|
array_intersect_key($info[0],
|
||
|
array_flip($this->args['attr'])))),TRUE);
|
||
|
}
|
||
|
user_error(self::E_LDAP,E_USER_ERROR);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* SMTP storage handler
|
||
|
* @return bool
|
||
|
* @param $id string
|
||
|
* @param $pw string
|
||
|
**/
|
||
|
protected function _smtp($id,$pw) {
|
||
|
$socket=@fsockopen(
|
||
|
(strtolower($this->args['scheme'])=='ssl'?
|
||
|
'ssl://':'').$this->args['host'],
|
||
|
$this->args['port']);
|
||
|
$dialog=function($cmd=NULL) use($socket) {
|
||
|
if (!is_null($cmd))
|
||
|
fputs($socket,$cmd."\r\n");
|
||
|
$reply='';
|
||
|
while (!feof($socket) &&
|
||
|
($info=stream_get_meta_data($socket)) &&
|
||
|
!$info['timed_out'] && $str=fgets($socket,4096)) {
|
||
|
$reply.=$str;
|
||
|
if (preg_match('/(?:^|\n)\d{3} .+\r\n/s',
|
||
|
$reply))
|
||
|
break;
|
||
|
}
|
||
|
return $reply;
|
||
|
};
|
||
|
if ($socket) {
|
||
|
stream_set_blocking($socket,TRUE);
|
||
|
$dialog();
|
||
|
$fw=Base::instance();
|
||
|
$dialog('EHLO '.$fw->HOST);
|
||
|
if (strtolower($this->args['scheme'])=='tls') {
|
||
|
$dialog('STARTTLS');
|
||
|
stream_socket_enable_crypto(
|
||
|
$socket,TRUE,STREAM_CRYPTO_METHOD_TLS_CLIENT);
|
||
|
$dialog('EHLO '.$fw->HOST);
|
||
|
}
|
||
|
// Authenticate
|
||
|
$dialog('AUTH LOGIN');
|
||
|
$dialog(base64_encode($id));
|
||
|
$reply=$dialog(base64_encode($pw));
|
||
|
$dialog('QUIT');
|
||
|
fclose($socket);
|
||
|
return (bool)preg_match('/^235 /',$reply);
|
||
|
}
|
||
|
user_error(self::E_SMTP,E_USER_ERROR);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Login auth mechanism
|
||
|
* @return bool
|
||
|
* @param $id string
|
||
|
* @param $pw string
|
||
|
* @param $realm string
|
||
|
**/
|
||
|
function login($id,$pw,$realm=NULL) {
|
||
|
return $this->{'_'.$this->storage}($id,$pw,$realm);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* HTTP basic auth mechanism
|
||
|
* @return bool
|
||
|
* @param $func callback
|
||
|
**/
|
||
|
function basic($func=NULL) {
|
||
|
$fw=Base::instance();
|
||
|
$realm=$fw->REALM;
|
||
|
$hdr=NULL;
|
||
|
if (isset($_SERVER['HTTP_AUTHORIZATION']))
|
||
|
$hdr=$_SERVER['HTTP_AUTHORIZATION'];
|
||
|
elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
|
||
|
$hdr=$_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
|
||
|
if (!empty($hdr))
|
||
|
list($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW'])=
|
||
|
explode(':',base64_decode(substr($hdr,6)));
|
||
|
if (isset($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']) &&
|
||
|
$this->login(
|
||
|
$_SERVER['PHP_AUTH_USER'],
|
||
|
$func?
|
||
|
$fw->call($func,$_SERVER['PHP_AUTH_PW']):
|
||
|
$_SERVER['PHP_AUTH_PW'],
|
||
|
$realm
|
||
|
))
|
||
|
return TRUE;
|
||
|
if (PHP_SAPI!='cli')
|
||
|
header('WWW-Authenticate: Basic realm="'.$realm.'"');
|
||
|
$fw->status(401);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Instantiate class
|
||
|
* @return object
|
||
|
* @param $storage string|object
|
||
|
* @param $args array
|
||
|
* @param $func callback
|
||
|
**/
|
||
|
function __construct($storage,array $args=NULL,$func=NULL) {
|
||
|
if (is_object($storage) && is_a($storage,'DB\Cursor')) {
|
||
|
$this->storage=$storage->dbtype();
|
||
|
$this->mapper=$storage;
|
||
|
unset($ref);
|
||
|
}
|
||
|
else
|
||
|
$this->storage=$storage;
|
||
|
$this->args=$args;
|
||
|
$this->func=$func;
|
||
|
}
|
||
|
|
||
|
}
|