Test Failed
Push — master ( 58150d...65223e )
by
unknown
23:33
created

ApiBase::isAuthPass()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 8
ccs 0
cts 5
cp 0
crap 6
rs 10
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
The condition is_null($rut) is always false.
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
introduced by
The condition is_null($firmaElectronicaBase64) is always false.
Loading history...
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
introduced by
The condition is_null($pemStr) is always false.
Loading history...
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