Passed
Push — master ( c89864...65eac8 )
by Jan
17:55 queued 12s
created

UserProvider::getImpersonatorUser()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 4
nc 2
nop 0
dl 0
loc 10
rs 10
c 1
b 0
f 0
1
<?php
2
/*
3
 * Copyright (C) 2020  Jan Böhmer
4
 *
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU Affero General Public License as published
7
 * by the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU Affero General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Affero General Public License
16
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
 */
18
19
namespace App\Audit;
20
21
22
use DH\Auditor\Event\LifecycleEvent;
23
use DH\Auditor\Provider\Doctrine\Configuration;
24
use DH\Auditor\User\User;
25
use DH\Auditor\User\UserInterface as AuditorUserInterface;
26
use DH\Auditor\User\UserProviderInterface;
27
use Doctrine\Common\EventSubscriber;
28
use Doctrine\ORM\Event\LifecycleEventArgs;
29
use Doctrine\ORM\Event\PostFlushEventArgs;
30
use Doctrine\ORM\Events;
31
use Exception;
32
use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
33
use Symfony\Component\Security\Core\Security;
34
use Symfony\Component\Security\Core\User\UserInterface;
35
36
class UserProvider implements UserProviderInterface, EventSubscriber
37
{
38
    public const CLI_USER_IDENTIFER = '$cli';
39
    public const INTERNAL_USER_IDENTIFIER = '$internal';
40
41
    /** @var string|null */
42
    private $username;
43
44
    /** @var string|null */
45
    private $identifier;
46
47
    /**
48
     * @var Security
49
     */
50
    private $security;
51
52
    /**
53
     * @var Configuration
54
     */
55
    private $configuration;
56
57
    public function __construct(Security $security, Configuration $configuration)
58
    {
59
        $this->security = $security;
60
        $this->configuration = $configuration;
61
    }
62
63
    public function setManualUsername(?string $username, ?string $identifier): void
64
    {
65
        $this->username = $username;
66
        $this->identifier = $identifier;
67
    }
68
69
    public function postFlush(PostFlushEventArgs $args): void
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

69
    public function postFlush(/** @scrutinizer ignore-unused */ PostFlushEventArgs $args): void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
70
    {
71
        //Reset manual set username and identifier after flush
72
        $this->username = null;
73
        $this->identifier = null;
74
    }
75
76
    public function __invoke(): ?AuditorUserInterface
77
    {
78
        $tokenUser = $this->getTokenUser();
79
        $impersonatorUser = $this->getImpersonatorUser();
80
81
        $identifier = null;
82
        $username = null;
83
84
        if (null !== $tokenUser && $tokenUser instanceof UserInterface) {
85
            //Use full name of the user if possible
86
            if ($tokenUser instanceof \App\Entity\User) {
87
                $identifier = $tokenUser->getUsername();
88
                $username = (string) $tokenUser;
89
            } else {
90
                if (method_exists($tokenUser, 'getId')) {
91
                    $identifier = $tokenUser->getId();
92
                }
93
94
                $username = $tokenUser->getUsername();
95
            }
96
        }
97
98
        if (null !== $impersonatorUser && $impersonatorUser instanceof UserInterface) {
99
            $username .= sprintf('[impersonator %s]', $impersonatorUser->getUsername());
100
        }
101
102
        //Check if a username and identifier were manually provided
103
        if (!empty($this->username) && !empty($this->identifier)) {
104
            $username = $this->username;
105
            $identifier = $this->identifier;
106
        } elseif ($this->is_cli()) { //Check if we are on command line, then use the username of the user
107
            $identifier = self::CLI_USER_IDENTIFER;
108
            $username = 'CLI';
109
            if (function_exists('posix_getpwuid') && function_exists('posix_geteuid')) {
110
                $username = sprintf('CLI [%s]', posix_getpwuid(posix_geteuid())['name']);
111
            }
112
        }
113
114
        if (null === $identifier && null === $username) {
115
            return null;
116
        }
117
118
        return new User($identifier, $username);
119
    }
120
121
    private function is_cli(): bool
122
    {
123
        if ( defined('STDIN') )
124
        {
125
            return true;
126
        }
127
128
        if ( php_sapi_name() === 'cli' )
129
        {
130
            return true;
131
        }
132
133
        if ( array_key_exists('SHELL', $_ENV) ) {
134
            return true;
135
        }
136
137
        if ( empty($_SERVER['REMOTE_ADDR']) and !isset($_SERVER['HTTP_USER_AGENT']) and count($_SERVER['argv']) > 0)
138
        {
139
            return true;
140
        }
141
142
        if ( !array_key_exists('REQUEST_METHOD', $_SERVER) )
143
        {
144
            return true;
145
        }
146
147
        return false;
148
    }
149
150
    /**
151
     * @return null|UserInterface
152
     */
153
    private function getTokenUser()
154
    {
155
        try {
156
            $token = $this->security->getToken();
157
        } catch (Exception $e) {
158
            $token = null;
159
        }
160
161
        if (null === $token) {
162
            return null;
163
        }
164
165
        $tokenUser = $token->getUser();
166
        if ($tokenUser instanceof UserInterface) {
167
            return $tokenUser;
168
        }
169
170
        return null;
171
    }
172
173
    /**
174
     * @return null|string|UserInterface
175
     */
176
    private function getImpersonatorUser()
177
    {
178
        $token = $this->security->getToken();
179
180
        // Symfony >= 5
181
        if (class_exists(SwitchUserToken::class) && $token instanceof SwitchUserToken) {
182
            return $token->getOriginalToken()->getUser();
183
        }
184
185
        return null;
186
    }
187
188
    public function getSubscribedEvents()
189
    {
190
        return [
191
            Events::postFlush
192
        ];
193
    }
194
}