apigatewaycl /
apigateway-api-client-php
| 1 | <?php |
||
| 2 | |||
| 3 | declare(strict_types=1); |
||
| 4 | |||
| 5 | /** |
||
| 6 | * API Gateway: Cliente de API en PHP. |
||
| 7 | * Copyright (C) API Gateway <https://www.apigateway.cl> |
||
| 8 | * |
||
| 9 | * Este programa es software libre: usted puede redistribuirlo y/o modificarlo |
||
| 10 | * bajo los términos de la GNU Lesser General Public License (LGPL) publicada |
||
| 11 | * por la Fundación para el Software Libre, ya sea la versión 3 de la Licencia, |
||
| 12 | * o (a su elección) cualquier versión posterior de la misma. |
||
| 13 | * |
||
| 14 | * Este programa se distribuye con la esperanza de que sea útil, pero SIN |
||
| 15 | * GARANTÍA ALGUNA; ni siquiera la garantía implícita MERCANTIL o de APTITUD |
||
| 16 | * PARA UN PROPÓSITO DETERMINADO. Consulte los detalles de la GNU Lesser General |
||
| 17 | * Public License (LGPL) para obtener una información más detallada. |
||
| 18 | * |
||
| 19 | * Debería haber recibido una copia de la GNU Lesser General Public License |
||
| 20 | * (LGPL) junto a este programa. En caso contrario, consulte |
||
| 21 | * <http://www.gnu.org/licenses/lgpl.html>. |
||
| 22 | */ |
||
| 23 | |||
| 24 | namespace apigatewaycl\api_client; |
||
| 25 | |||
| 26 | /** |
||
| 27 | * Clase base para las clases que consumen la API (wrappers). |
||
| 28 | */ |
||
| 29 | class ApiBase extends ApiClient |
||
| 30 | { |
||
| 31 | /** |
||
| 32 | * Arreglo que contendrá el diccionario de autenticación. |
||
| 33 | * |
||
| 34 | * @var array |
||
| 35 | */ |
||
| 36 | protected $auth = []; |
||
| 37 | |||
| 38 | /** |
||
| 39 | * Constructor de la clase base. |
||
| 40 | * |
||
| 41 | * @param array $credenciales Argumentos adicionales para la autenticación. |
||
| 42 | * @param string|null $token Token de autenticación para la API. |
||
| 43 | * @param string|null $url URL base para la API. |
||
| 44 | */ |
||
| 45 | public function __construct( |
||
| 46 | array $credenciales = [], |
||
| 47 | string $token = null, |
||
| 48 | string $url = null |
||
| 49 | ) { |
||
| 50 | parent::__construct($token, $url); |
||
| 51 | $this->setupAuth($credenciales); |
||
| 52 | } |
||
| 53 | |||
| 54 | /** |
||
| 55 | * Configura la autenticación específica para la aplicación. |
||
| 56 | * |
||
| 57 | * @param array $credenciales Parámetros de autenticación. Puede ser |
||
| 58 | * 'pass' o 'cert'. |
||
| 59 | * @throws \apigatewaycl\api_client\ApiException |
||
| 60 | * @return void |
||
| 61 | */ |
||
| 62 | private function setupAuth(array $credenciales): void |
||
| 63 | { |
||
| 64 | $tipo = key($credenciales); // Detecta si es 'pass' o 'cert' |
||
| 65 | $datos = $credenciales[$tipo] ?? []; |
||
| 66 | |||
| 67 | $identificador = $datos['rut'] ?? |
||
| 68 | $datos['cert-data'] ?? |
||
| 69 | $datos['file-data'] ?? |
||
| 70 | null; |
||
| 71 | $clave = $datos['clave'] ?? |
||
| 72 | $datos['pkey-data'] ?? |
||
| 73 | $datos['file-pass'] ?? |
||
| 74 | null; |
||
| 75 | |||
| 76 | if ($identificador && $clave) { |
||
| 77 | if ($this->isAuthPass($identificador)) { |
||
| 78 | $this->auth = [ |
||
| 79 | 'pass' => [ |
||
| 80 | 'rut' => $identificador, |
||
| 81 | 'clave' => $clave, |
||
| 82 | ], |
||
| 83 | ]; |
||
| 84 | } elseif ($this->isAuthCertData($identificador)) { |
||
| 85 | $this->auth = [ |
||
| 86 | 'cert' => [ |
||
| 87 | 'cert-data' => $identificador, |
||
| 88 | 'pkey-data' => $clave, |
||
| 89 | ], |
||
| 90 | ]; |
||
| 91 | } elseif ($this->isAuthFileData($identificador)) { |
||
| 92 | $this->auth = [ |
||
| 93 | 'cert' => [ |
||
| 94 | 'file-data' => $identificador, |
||
| 95 | 'file-pass' => $clave, |
||
| 96 | ], |
||
| 97 | ]; |
||
| 98 | } else { |
||
| 99 | throw new ApiException( |
||
| 100 | message: 'No se han proporcionado las credenciales de autentificación.' |
||
| 101 | ); |
||
| 102 | } |
||
| 103 | } |
||
| 104 | } |
||
| 105 | |||
| 106 | /** |
||
| 107 | * Valida la estructura de un RUT chileno utilizando una expresión regular. |
||
| 108 | * |
||
| 109 | * Este método verifica que el RUT cumpla con el formato estándar chileno, que incluye |
||
| 110 | * puntos como separadores de miles opcionales y un guion antes del dígito verificador. |
||
| 111 | * El dígito verificador puede ser un número o la letra 'K'. |
||
| 112 | * |
||
| 113 | * **Ejemplos de RUT válidos:** |
||
| 114 | * |
||
| 115 | * - 12.345.678-5 |
||
| 116 | * - 12345678-5 |
||
| 117 | * - 9.876.543-K |
||
| 118 | * - 9876543-K |
||
| 119 | * |
||
| 120 | * **Ejemplos de RUT inválidos:** |
||
| 121 | * |
||
| 122 | * - 12.345.678-9 (dígito verificador incorrecto) |
||
| 123 | * - 12345678- (falta dígito verificador) |
||
| 124 | * - 12345-6 (formato incorrecto) |
||
| 125 | * - 12.345.6785 (falta guion) |
||
| 126 | * - abcdefgh-i (caracteres no permitidos) |
||
| 127 | * |
||
| 128 | * @param string $rut El RUT a validar. |
||
| 129 | * @return bool true si el RUT tiene un formato válido,false en caso |
||
| 130 | * contrario. |
||
| 131 | */ |
||
| 132 | private function isAuthPass(string $rut): bool |
||
| 133 | { |
||
| 134 | if (is_null($rut)) { |
||
|
0 ignored issues
–
show
introduced
by
Loading history...
|
|||
| 135 | return false; |
||
| 136 | } |
||
| 137 | // Expresión regular para validar el formato del RUT chileno |
||
| 138 | $pattern = '/^(\d{1,3}\.?)(\d{3}\.?)(\d{3,4})-([\dkK])$/'; |
||
| 139 | return preg_match($pattern, $rut) === 1; |
||
| 140 | } |
||
| 141 | |||
| 142 | /** |
||
| 143 | * Verifica si una cadena es una cadena codificada en Base64 válida. |
||
| 144 | * |
||
| 145 | * @param string $firmaElectronicaBase64 La cadena a verificar. |
||
| 146 | * @return bool true si la cadena es válida en Base64, false en caso |
||
| 147 | * contrario. |
||
| 148 | */ |
||
| 149 | private function isAuthFileData(string $firmaElectronicaBase64): bool |
||
| 150 | { |
||
| 151 | if (is_null($firmaElectronicaBase64)) { |
||
|
0 ignored issues
–
show
|
|||
| 152 | return false; |
||
| 153 | } |
||
| 154 | // Asegúrate de que la longitud de la cadena sea múltiplo de 4 |
||
| 155 | if (strlen($firmaElectronicaBase64) % 4 !== 0) { |
||
| 156 | return false; |
||
| 157 | } |
||
| 158 | // Validar Base64 |
||
| 159 | return base64_decode( |
||
| 160 | string: $firmaElectronicaBase64, |
||
| 161 | strict: true |
||
| 162 | ) !== false; |
||
| 163 | } |
||
| 164 | |||
| 165 | /** |
||
| 166 | * Valida si una cadena tiene formato PEM válido. |
||
| 167 | * |
||
| 168 | * El formato PEM debe cumplir con los siguientes criterios: |
||
| 169 | * |
||
| 170 | * - Comienza con una línea "-----BEGIN [LABEL]-----" |
||
| 171 | * - Termina con una línea "-----END [LABEL]-----" |
||
| 172 | * - Contiene contenido Base64 válido entre las líneas BEGIN y END |
||
| 173 | * |
||
| 174 | * **Ejemplos de PEM Válidos:** |
||
| 175 | * |
||
| 176 | * ``` |
||
| 177 | * -----BEGIN CERTIFICATE----- |
||
| 178 | * MIIDdzCCAl+gAwIBAgIEbGzVnzANBgkqhkiG9w0BAQsFADBvMQswCQYDVQQGEwJV |
||
| 179 | * ... |
||
| 180 | * -----END CERTIFICATE----- |
||
| 181 | * ``` |
||
| 182 | * |
||
| 183 | * **Ejemplos de PEM Inválidos:** |
||
| 184 | * |
||
| 185 | * - Falta la línea de inicio o fin. |
||
| 186 | * - Contenido no codificado en Base64. |
||
| 187 | * - Etiquetas de BEGIN y END que no coinciden. |
||
| 188 | * |
||
| 189 | * @param string $pemStr La cadena a validar. |
||
| 190 | * @return bool true si la cadena tiene formato PEM válido, false en caso contrario. |
||
| 191 | */ |
||
| 192 | private function isAuthCertData(string $pemStr): bool |
||
| 193 | { |
||
| 194 | if (is_null($pemStr)) { |
||
|
0 ignored issues
–
show
|
|||
| 195 | return false; |
||
| 196 | } |
||
| 197 | // Expresión regular para validar el formato PEM |
||
| 198 | $pattern = '/-----BEGIN ([A-Z ]+)-----\s+([A-Za-z0-9+\/=\s]+)-----END \1-----$/m'; |
||
| 199 | if (!preg_match( |
||
| 200 | $pattern, |
||
| 201 | trim($pemStr), |
||
| 202 | $matches |
||
| 203 | ) |
||
| 204 | ) { |
||
| 205 | return false; |
||
| 206 | } |
||
| 207 | // Validar contenido Base64 |
||
| 208 | $base64Content = preg_replace( |
||
| 209 | '/\s+/', |
||
| 210 | '', |
||
| 211 | $matches[2] |
||
| 212 | ); |
||
| 213 | return base64_decode($base64Content, true) !== false; |
||
| 214 | } |
||
| 215 | |||
| 216 | /** |
||
| 217 | * Obtiene la autenticación de tipo 'pass'. |
||
| 218 | * |
||
| 219 | * @throws \apigatewaycl\api_client\ApiException Si falta información |
||
| 220 | * de autenticación. |
||
| 221 | * @return array Información de autenticación. |
||
| 222 | */ |
||
| 223 | protected function getAuthPass(): array |
||
| 224 | { |
||
| 225 | if (isset($this->auth['pass'])) { |
||
| 226 | if (empty($this->auth['pass']['rut'])) { |
||
| 227 | throw new ApiException(message: 'auth.pass.rut empty.'); |
||
| 228 | } |
||
| 229 | if (empty($this->auth['pass']['clave'])) { |
||
| 230 | throw new ApiException(message: 'auth.pass.clave empty.'); |
||
| 231 | } |
||
| 232 | } elseif (isset($this->auth['cert'])) { |
||
| 233 | if ( |
||
| 234 | isset($this->auth['cert']['cert-data']) && |
||
| 235 | empty($this->auth['cert']['cert-data']) |
||
| 236 | ) { |
||
| 237 | throw new ApiException(message: 'auth.cert.cert-data empty.'); |
||
| 238 | } |
||
| 239 | if ( |
||
| 240 | isset($this->auth['cert']['pkey-data']) && |
||
| 241 | empty($this->auth['cert']['pkey-data']) |
||
| 242 | ) { |
||
| 243 | throw new ApiException(message: 'auth.cert.pkey-data empty.'); |
||
| 244 | } |
||
| 245 | if ( |
||
| 246 | isset($this->auth['cert']['file-data']) && |
||
| 247 | empty($this->auth['cert']['file-data']) |
||
| 248 | ) { |
||
| 249 | throw new ApiException(message: 'auth.cert.file-data empty.'); |
||
| 250 | } |
||
| 251 | if ( |
||
| 252 | isset($this->auth['cert']['file-pass']) && |
||
| 253 | empty($this->auth['cert']['file-pass']) |
||
| 254 | ) { |
||
| 255 | throw new ApiException(message: 'auth.cert.file-pass empty.'); |
||
| 256 | } |
||
| 257 | } else { |
||
| 258 | throw new ApiException(message: 'auth.pass or auth.cert missing.'); |
||
| 259 | } |
||
| 260 | |||
| 261 | return $this->auth; |
||
| 262 | } |
||
| 263 | } |
||
| 264 |