PageRenderTime 42ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/core/libs/auth/auth.php

http://github.com/KumbiaPHP/KumbiaPHP
PHP | 332 lines | 153 code | 27 blank | 152 comment | 14 complexity | f131f0671bc666ed16265650df0eec00 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * KumbiaPHP web & app Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.
  9. *
  10. * @category Kumbia
  11. * @package Auth
  12. *
  13. * @copyright Copyright (c) 2005 - 2020 KumbiaPHP Team (http://www.kumbiaphp.com)
  14. * @license https://github.com/KumbiaPHP/KumbiaPHP/blob/master/LICENSE New BSD License
  15. */
  16. /**
  17. * @see AuthInterface
  18. */
  19. require_once __DIR__.'/auth_interface.php';
  20. // Evita problemas al actualizar de la beta2
  21. if (session_status() !== PHP_SESSION_ACTIVE) {
  22. session_start();
  23. }
  24. /**
  25. * Esta clase permite autenticar usuarios.
  26. *
  27. * @category Kumbia
  28. * @package Auth
  29. */
  30. class Auth
  31. {
  32. /**
  33. * Nombres para crear las sessiones.
  34. *
  35. */
  36. const IDENTITY = 'KUMBIA_AUTH_IDENTITY';
  37. const VALID = 'KUMBIA_AUTH_VALID';
  38. /**
  39. * Nombre del adaptador usado para autenticar.
  40. *
  41. * @var string
  42. */
  43. private $adapter;
  44. /**
  45. * Objeto Adaptador actual.
  46. *
  47. * @var mixed
  48. */
  49. private $adapter_object;
  50. /**
  51. * Indica si un usuario debe loguearse sólo una vez en el sistema desde
  52. * cualquier parte.
  53. *
  54. * @var bool
  55. */
  56. private $active_session = false;
  57. /**
  58. * Tiempo en que expirará la sesion en caso de que no se termine con destroy_active_session.
  59. *
  60. * @var int
  61. */
  62. private $expire_time = 3600;
  63. /**
  64. * Argumentos extra enviados al Adaptador.
  65. *
  66. * @var array
  67. */
  68. private $extra_args = array();
  69. /**
  70. * Usar la misma sesion para las applicaciones con el mismo namespace en config.
  71. * de config.application.namespace_auth
  72. *
  73. * @var string
  74. */
  75. private static $app_namespace;
  76. /**
  77. * Indica si la última llamada a authenticate tuvo éxito o no (persistente en sesion).
  78. *
  79. * @var bool|null
  80. */
  81. private static $is_valid = null;
  82. /**
  83. * Última identidad obtenida por Authenticate (persistente en sesion).
  84. *
  85. * @var array
  86. */
  87. private static $active_identity = array();
  88. /**
  89. * Constructor del Autenticador.
  90. */
  91. public function __construct()
  92. {
  93. $adapter = 'model'; //default
  94. $extra_args = Util::getParams(func_get_args());
  95. if (isset($extra_args[0])) {
  96. $adapter = $extra_args[0];
  97. unset($extra_args[0]);
  98. }
  99. $this->set_adapter($adapter, $this, $extra_args);
  100. self::$app_namespace = Config::get('config.application.namespace_auth');
  101. }
  102. /**
  103. * Modifica el adaptador a usar.
  104. *
  105. * @param string $adapter Tipo de adaptador a usar ('digest', 'model', 'kerberos5', 'radius')
  106. * @param Auth $auth Instancia de la clase Auth
  107. * @param array $extra_args Argumentos adicionales
  108. * @throws kumbiaException
  109. */
  110. public function set_adapter($adapter, $auth = '', $extra_args = array())
  111. {
  112. if (!in_array($adapter, array('digest', 'model', 'kerberos5', 'radius'))) {
  113. throw new kumbiaException("Adaptador de autenticación '$adapter' no soportado");
  114. }
  115. $this->adapter = Util::camelcase($adapter);
  116. require_once __DIR__."/adapters/{$adapter}_auth.php";
  117. $adapter_class = $this->adapter.'Auth';
  118. $this->extra_args = $extra_args;
  119. $this->adapter_object = new $adapter_class($auth, $extra_args);
  120. }
  121. /**
  122. * Obtiene el nombre del adaptador actual.
  123. *
  124. * @return string
  125. */
  126. public function get_adapter_name()
  127. {
  128. return $this->adapter;
  129. }
  130. /**
  131. * Realiza el proceso de autenticación.
  132. *
  133. * @return array|bool
  134. */
  135. public function authenticate()
  136. {
  137. $result = $this->adapter_object->authenticate();
  138. /*
  139. * Si es una sesion activa maneja un archivo persistente para control
  140. */
  141. if ($result && $this->active_session) {
  142. $this->active_session();
  143. }
  144. $_SESSION[self::IDENTITY][self::$app_namespace] = $this->adapter_object->get_identity();
  145. self::$active_identity = $this->adapter_object->get_identity();
  146. $_SESSION[self::VALID][self::$app_namespace] = $result;
  147. self::$is_valid = $result;
  148. return $result;
  149. }
  150. /**
  151. * Si es una sesión activa maneja un archivo persistente para control.
  152. *
  153. * TODO usar sqlite
  154. */
  155. private function active_session()
  156. {
  157. $user_hash = md5(serialize($this->extra_args));
  158. $filename = APP_PATH.'temp/cache/'.base64_encode('auth');
  159. if (is_file($filename)) {
  160. $fp = fopen($filename, 'r');
  161. while (!feof($fp)) {
  162. $line = fgets($fp);
  163. $user = explode(':', $line);
  164. if ($user_hash === $user[0]) {
  165. if ($user[1] + $user[2] > time()) {
  166. self::$active_identity = array();
  167. self::$is_valid = false;
  168. return false;
  169. }
  170. fclose($fp);
  171. $this->destroy_active_session();
  172. file_put_contents($filename, $user_hash.':'.time().':'.$this->expire_time."\n");
  173. }
  174. }
  175. fclose($fp);
  176. $fp = fopen($filename, 'a');
  177. fputs($fp, $user_hash.':'.time().':'.$this->expire_time."\n");
  178. fclose($fp);
  179. return;
  180. }
  181. file_put_contents($filename, $user_hash.':'.time().':'.$this->expire_time."\n");
  182. }
  183. /**
  184. * Realiza el proceso de autenticación usando HTTP.
  185. *
  186. * @return array
  187. */
  188. public function authenticate_with_http()
  189. {
  190. if (!$_SERVER['PHP_AUTH_USER']) {
  191. header('WWW-Authenticate: Basic realm="basic"');
  192. http_response_code(401);
  193. return false;
  194. }
  195. $options = array('username' => $_SERVER['PHP_AUTH_USER'], 'password' => $_SERVER['PHP_AUTH_PW']);
  196. $this->adapter_object->set_params($options);
  197. return $this->authenticate();
  198. }
  199. /**
  200. * Devuelve la identidad encontrada en caso de exito.
  201. *
  202. * @return array
  203. */
  204. public function get_identity()
  205. {
  206. return $this->adapter_object->get_identity();
  207. }
  208. /**
  209. * Permite controlar que usuario no se loguee más de una vez en el
  210. * sistema desde cualquier parte.
  211. *
  212. * @param bool $value En true para activar la validación
  213. * @param int $time Tiempo en el que expirará la sesión
  214. */
  215. public function set_active_session($value, $time = 3600)
  216. {
  217. $this->active_session = $value;
  218. $this->expire_time = $time;
  219. }
  220. /**
  221. * Permite destruir sesion activa del usuario autenticado.
  222. */
  223. public function destroy_active_session()
  224. {
  225. $user_hash = md5(serialize($this->extra_args));
  226. $filename = APP_PATH.'temp/cache/'.base64_encode('auth');
  227. $lines = file($filename);
  228. $lines_out = array();
  229. foreach ($lines as $line) {
  230. if (substr($line, 0, 32) !== $user_hash) {
  231. $lines_out[] = $line;
  232. }
  233. }
  234. file_put_contents($filename, join("\n", $lines_out));
  235. }
  236. /**
  237. * Devuelve la instancia del adaptador.
  238. *
  239. * @return mixed Objeto Adaptador actual.
  240. */
  241. public function get_adapter_instance()
  242. {
  243. return $this->adapter_object;
  244. }
  245. /**
  246. * Determinar si debe dormir la aplicación cuando falle la autenticación y cuanto tiempo en segundos.
  247. *
  248. * @param bool $value
  249. * @param int $time
  250. *
  251. * @deprecated se mantiene para no romper apps
  252. */
  253. public function sleep_on_fail($value, $time = 2)
  254. {
  255. throw new KumbiaException("El método sleep_on_fail($value, $time) de la clase Auth está desaconsejado. Borrar de su código.");
  256. }
  257. /**
  258. * Devuelve si es un usuario válido.
  259. *
  260. * @return bool
  261. */
  262. public static function is_valid()
  263. {
  264. if (!is_null(self::$is_valid)) {
  265. return self::$is_valid;
  266. }
  267. self::$is_valid = isset($_SESSION[self::VALID][Config::get('config.application.namespace_auth')]) ? $_SESSION[self::VALID][Config::get('config.application.namespace_auth')] : null;
  268. return self::$is_valid;
  269. }
  270. /**
  271. * Devuelve el resultado de la ultima identidad obtenida en authenticate
  272. * desde el ultimo objeto Auth instanciado.
  273. *
  274. * @return array
  275. */
  276. public static function get_active_identity()
  277. {
  278. if (count(self::$active_identity)) {
  279. return self::$active_identity;
  280. }
  281. return self::$active_identity = $_SESSION[self::IDENTITY][Config::get('config.application.namespace_auth')];
  282. }
  283. /**
  284. * Obtiene un valor de la identidad actual.
  285. *
  286. * @param string $var Llave que identifica el valor
  287. * @return string Valor de la llave
  288. */
  289. public static function get($var)
  290. {
  291. if (isset($_SESSION[self::IDENTITY][Config::get('config.application.namespace_auth')][$var])) {
  292. return $_SESSION[self::IDENTITY][Config::get('config.application.namespace_auth')][$var];
  293. }
  294. }
  295. /**
  296. * Anula la identidad actual.
  297. */
  298. public static function destroy_identity()
  299. {
  300. self::$is_valid = null;
  301. unset($_SESSION['KUMBIA_AUTH_VALID'][Config::get('config.application.namespace_auth')]);
  302. self::$active_identity = array();
  303. unset($_SESSION[self::IDENTITY][Config::get('config.application.namespace_auth')]);
  304. }
  305. }