Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
84.62% covered (success)
84.62%
22 / 26
66.67% covered (warning)
66.67%
6 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
Auth
84.62% covered (success)
84.62%
22 / 26
66.67% covered (warning)
66.67%
6 / 9
15.82
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getEncryptionContainer
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getErrors
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 addError
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 selectAuth
80.00% covered (success)
80.00%
8 / 10
0.00% covered (danger)
0.00%
0 / 1
4.13
 enableReadOnly
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 disableReadOnly
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 isReadOnly
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 encrypt
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
1<?php
2
3/**
4 * Manages the user authentication in phpMyFAQ
5 *
6 * Subclasses of Auth implement authentication functionality with different types. The class AuthLdap for example
7 * provides database access. Authentication functionality includes creation of a new login-and-password, deletion
8 * of an existing login-and-password combination, and validation of given by a user.
9 * These functions are provided by the database-specific see documentation of the database-specific authentication
10 * classes AuthDatabase, or AuthLdap for further details. Passwords are usually encrypted before stored in a database.
11 * For security, a password encryption method may be chosen. See documentation of Encryption class for further details.
12 *
13 * This Source Code Form is subject to the terms of the Mozilla Public License,
14 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
15 * obtain one at https://mozilla.org/MPL/2.0/.
16 *
17 * @package   phpMyFAQ
18 * @author    Lars Tiedemann <php@larstiedemann.de>
19 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
20 * @copyright 2005-2026 phpMyFAQ Team
21 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
22 * @link      https://www.phpmyfaq.de
23 * @since     2005-09-30
24 */
25
26declare(strict_types=1);
27
28namespace phpMyFAQ;
29
30use phpMyFAQ\Auth\AuthDriverInterface;
31use phpMyFAQ\Core\Exception;
32use SensitiveParameter;
33
34/**
35 * Class Auth
36 *
37 * Manages user authentication in phpMyFAQ. Concrete adapters (e.g., AuthDatabase, AuthLdap, AuthHttp)
38 * implement the actual operations like creating, deleting, and validating login/password pairs.
39 * Passwords are encrypted via an Encryption implementation configured at runtime.
40 */
41class Auth
42{
43    private const string PMF_ERROR_USER_NO_AUTH_TYPE = 'Specified authentication access class could not be found.';
44
45    /**
46     * Public array that contains error messages.
47     *
48     * @var array<string>
49     */
50    protected array $errors = [];
51
52    /**
53     * Container that stores the encryption object.
54     */
55    protected ?Encryption $encContainer = null;
56
57    /**
58     * Read-only flag.
59     */
60    private bool $readOnly = false;
61
62    /**
63     * Constructor.
64     */
65    public function __construct(
66        protected Configuration $configuration,
67    ) {
68    }
69
70    /**
71     * Instantiates an Encryption implementation based on the given type.
72     */
73    public function getEncryptionContainer(string $encType): Encryption
74    {
75        $this->encContainer = Encryption::getInstance($encType, $this->configuration);
76        return $this->encContainer;
77    }
78
79    /**
80     * Returns a string with all collected error messages, each separated by a new line.
81     */
82    public function getErrors(): string
83    {
84        $message = $this->errors !== [] ? implode(separator: PHP_EOL, array: $this->errors) . PHP_EOL : '';
85        return $message . ($this->encContainer instanceof \phpMyFAQ\Encryption ? $this->encContainer->error() : '');
86    }
87
88    /**
89     * Adds an error message to the list of errors.
90     */
91    public function addError(string $message): void
92    {
93        $this->errors[] = $message;
94    }
95
96    /**
97     * Returns an authentication object for the specified method.
98     *
99     * @throws Exception If the auth class cannot be found
100     */
101    public function selectAuth(string $method): Auth&AuthDriverInterface
102    {
103        $method = ucfirst(strtolower($method));
104        $authClass = '\\phpMyFAQ\\Auth\\Auth' . $method;
105
106        if (!class_exists($authClass) || !is_subclass_of($authClass, self::class)) {
107            $this->errors[] = self::PMF_ERROR_USER_NO_AUTH_TYPE;
108            throw new Exception(message: self::PMF_ERROR_USER_NO_AUTH_TYPE);
109        }
110
111        $auth = new $authClass($this->configuration);
112        if (!$auth instanceof AuthDriverInterface) {
113            $this->errors[] = self::PMF_ERROR_USER_NO_AUTH_TYPE;
114            throw new Exception(message: self::PMF_ERROR_USER_NO_AUTH_TYPE);
115        }
116
117        return $auth;
118    }
119
120    /**
121     * Enables read-only mode and returns the previous state.
122     */
123    public function enableReadOnly(): bool
124    {
125        $oldReadOnly = $this->readOnly;
126        $this->readOnly = true;
127
128        return $oldReadOnly;
129    }
130
131    /**
132     * Disables read-only mode and returns the previous state.
133     */
134    public function disableReadOnly(): bool
135    {
136        $oldReadOnly = $this->readOnly;
137        $this->readOnly = false;
138
139        return $oldReadOnly;
140    }
141
142    /**
143     * Returns the current read-only state.
144     */
145    public function isReadOnly(): bool
146    {
147        return $this->readOnly;
148    }
149
150    /**
151     * Encrypts a string using the configured encryption container.
152     *
153     * @throws Exception If no encryption container was configured
154     */
155    public function encrypt(#[SensitiveParameter] string $string): string
156    {
157        if (!$this->encContainer instanceof \phpMyFAQ\Encryption) {
158            throw new Exception(message: 'No encryption container configured. Call getEncryptionContainer() first.');
159        }
160
161        return $this->encContainer->encrypt($string);
162    }
163}