Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.44% covered (success)
94.44%
34 / 36
75.00% covered (warning)
75.00%
6 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
AuthSso
94.44% covered (success)
94.44%
34 / 36
75.00% covered (warning)
75.00%
6 / 8
18.06
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 create
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 update
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 delete
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 checkCredentials
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
5
 isValidLogin
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 createLdapAuth
80.00% covered (success)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 createUser
80.00% covered (success)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
1<?php
2
3/**
4 * Manages user authentication with Apache's SSO authentication, e.g. mod_sspi or mod_auth_kerb.
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @copyright 2011-2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2011-06-22
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Auth;
21
22use Closure;
23use phpMyFAQ\Auth;
24use phpMyFAQ\Configuration;
25use phpMyFAQ\Core\Exception;
26use phpMyFAQ\Enums\AuthenticationSourceType;
27use phpMyFAQ\User;
28use SensitiveParameter;
29use Symfony\Component\HttpFoundation\Request;
30
31/**
32 * Class Sso
33 *
34 * @package phpMyFAQ\Auth
35 */
36class AuthSso extends Auth implements AuthDriverInterface
37{
38    private readonly Request $request;
39    private readonly ?Closure $ldapFactory;
40    private readonly ?Closure $userFactory;
41
42    /**
43     * @inheritDoc
44     */
45    public function __construct(
46        Configuration $configuration,
47        ?Request $request = null,
48        ?Closure $ldapFactory = null,
49        ?Closure $userFactory = null,
50    ) {
51        parent::__construct($configuration);
52
53        $this->request = $request ?? Request::createFromGlobals();
54        $this->ldapFactory = $ldapFactory;
55        $this->userFactory = $userFactory;
56    }
57
58    /**
59     * @inheritDoc
60     * @throws Exception
61     */
62    public function create(string $login, #[SensitiveParameter] string $password, string $domain = ''): bool
63    {
64        if ($this->configuration->isLdapActive()) {
65            // LDAP/AD + SSO
66            $authLdap = $this->createLdapAuth();
67            return $authLdap->create($login, '', $domain);
68        }
69
70        // SSO without LDAP/AD
71        $user = $this->createUser();
72        $result = $user->createUser($login, '', $domain);
73        $user->setStatus('active');
74        $user->setAuthSource(AuthenticationSourceType::AUTH_SSO->value);
75        // Set user information
76        $user->setUserData(['display_name' => $login]);
77        return $result;
78    }
79
80    /**
81     * @inheritDoc
82     */
83    public function update(string $login, #[SensitiveParameter] string $password): bool
84    {
85        return true;
86    }
87
88    /**
89     * @inheritDoc
90     */
91    public function delete(string $login): bool
92    {
93        return true;
94    }
95
96    /**
97     * @inheritDoc
98     * @throws AuthException
99     * @throws Exception
100     */
101    public function checkCredentials(
102        string $login,
103        #[SensitiveParameter]
104        string $password,
105        ?array $optionalData = null,
106    ): bool {
107        if ($this->request->server->get('REMOTE_USER') === null) {
108            throw new AuthException('Remote User not set!');
109        }
110
111        // Check if "DOMAIN\user", "user@DOMAIN" or only "user"
112        $remote = explode('\\', (string) $this->request->server->get('REMOTE_USER'));
113        $user = $this->request->server->get('REMOTE_USER');
114        if (count($remote) > 1) {
115            $user = $remote[1];
116        }
117
118        if (count($remote) <= 1) {
119            $remote = explode('@', (string) $this->request->server->get('REMOTE_USER'));
120            $user = count($remote) > 1 ? $remote[0] : $this->request->server->get('REMOTE_USER');
121        }
122
123        return $user === $login;
124    }
125
126    /**
127     * @inheritDoc
128     */
129    public function isValidLogin(string $login, ?array $optionalData = null): int
130    {
131        return $this->request->server->get('PHP_AUTH_USER') !== null ? 1 : 0;
132    }
133
134    private function createLdapAuth(): AuthLdap
135    {
136        if ($this->ldapFactory instanceof Closure) {
137            $authLdap = ($this->ldapFactory)();
138            if ($authLdap instanceof AuthLdap) {
139                return $authLdap;
140            }
141        }
142
143        return new AuthLdap($this->configuration);
144    }
145
146    private function createUser(): User
147    {
148        if ($this->userFactory instanceof Closure) {
149            $user = ($this->userFactory)();
150            if ($user instanceof User) {
151                return $user;
152            }
153        }
154
155        return new User($this->configuration);
156    }
157}