Passed
Push — master ( 4cb925...613187 )
by Marek
10:16
created

AuthenticationService   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 201
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 201
c 0
b 0
f 0
wmc 24
lcom 1
cbo 9
ccs 74
cts 74
cp 1
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
A onApplicationInitialized() 0 9 2
A validateTicketTrackingPlatform() 0 19 3
B validateGitPlatform() 0 29 3
B validateFilesystem() 0 33 4
A hasProperAccess() 0 6 3
A createUrl() 0 8 1
A createDirectories() 0 5 2
A createFiles() 0 10 3
A removeFilesAndDirs() 0 9 2
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace AppBuilder\Application\Configuration;
6
7
use AppBuilder\Application\Configuration\ValueObject\Parameters;
8
use AppBuilder\Application\Module\HttpClient\ExternalLibraryHttpClient;
9
use AppBuilder\Application\Module\Jira\QueryRepository;
10
use AppBuilder\Application\Utils\FileManager\FileManagerService;
11
use AppBuilder\Event\Application\ApplicationInitializedEvent;
12
use AppBuilder\Event\Application\ApplicationInitializedEventAware;
13
use AppBuilder\Event\Application\CredentialsValidatedEvent;
14
use Exception;
15
use FileNotFoundException;
16
use GuzzleHttp\Exception\ClientException;
17
use GuzzleHttp\Exception\ConnectException;
18
use Psr\Log\LoggerInterface;
19
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
20
use Symfony\Component\Filesystem\Exception\IOException;
21
22
class AuthenticationService implements Authentication, ApplicationInitializedEventAware
23
{
24
    private const HTTP_OK = 200;
25
    /** @var QueryRepository */
26
    private $queryRepository;
27
28
    /** @var ExternalLibraryHttpClient */
29
    private $httpClient;
30
31
    /** @var FileManagerService */
32
    private $fileManager;
33
34
    /** @var LoggerInterface */
35
    private $logger;
36
37
    /** @var EventDispatcherInterface */
38
    private $dispatcher;
39
40 10
    public function __construct(
41
        QueryRepository $queryRepository,
42
        ExternalLibraryHttpClient $httpClient,
43
        FileManagerService $fileManager,
44
        LoggerInterface $logger,
45
        EventDispatcherInterface $dispatcher
46
    ) {
47 10
        $this->queryRepository = $queryRepository;
48 10
        $this->httpClient      = $httpClient;
49 10
        $this->fileManager     = $fileManager;
50 10
        $this->logger          = $logger;
51 10
        $this->dispatcher      = $dispatcher;
52 10
    }
53
54 1
    public function onApplicationInitialized(ApplicationInitializedEvent $event = null) : void
55
    {
56 1
        if ($this->hasProperAccess()) {
57 1
            $this->dispatcher->dispatch(
58 1
                CredentialsValidatedEvent::NAME,
59 1
                new CredentialsValidatedEvent()
60
            );
61
        }
62 1
    }
63
64
    /**
65
     * Checks wether you have permission to access chosen ticket tracking platform.
66
     */
67 4
    public function validateTicketTrackingPlatform() : bool
68
    {
69
        try {
70 4
            $this->httpClient->request(
71 4
                ExternalLibraryHttpClient::GET,
72 4
                $this->createUrl($this->queryRepository->validateCredentials())
73
            );
74 2
        } catch (ClientException $exception) {
75 1
            $this->logger->critical('Invalid login or password');
76
77 1
            return false;
78 1
        } catch (ConnectException $exception) {
79 1
            $this->logger->critical('Invalid host');
80
81 1
            return false;
82
        }
83
84 2
        return true;
85
    }
86
87
    /**
88
     * Checks wether you have permission to access chosen git platform.
89
     *
90
     * @throws ClientException
91
     */
92 4
    public function validateGitPlatform() : bool
93
    {
94
        /** @var string */
95 4
        $bitbucketPath = '/bitbucket';
96
97
        try {
98 4
            $response = $this->httpClient->request(
99 4
                ExternalLibraryHttpClient::GET,
100
                $bitbucketPath
101
            );
102
103 3
            if (self::HTTP_OK !== $response->getStatusCode()) {
104 1
                $this->logger->warning('Failed to login Bitbucket.');
105
106 3
                return false;
107
            }
108 1
        } catch (ClientException $exception) {
109 1
            $this->logger->warning(
110
                sprintf(
111 1
                    'Failed to login Bitbucket. %s ',
112 1
                    $exception->getMessage()
113
                )
114
            );
115
116 1
            return false;
117
        }
118
119 2
        return true;
120
    }
121
122
    /**
123
     * Checks wether you have permission to access filesystem.
124
     */
125 4
    public function validateFilesystem() : bool
126
    {
127
        /** @var Parameters */
128 4
        $applicationParams = $this->httpClient->applicationParams();
129
130
        /** @var string */
131 4
        $fakeKey = 'fakeKey';
132
133
        /** @var string */
134 4
        $projectHomedir = $applicationParams->path($fakeKey);
135
136
        /** @var string */
137 4
        $snapshot = $applicationParams->snapshotPath('fakeKey');
138
139
        /** @var string */
140 4
        $symlinkTarget = $applicationParams->symlinkTarget($fakeKey);
141
142
        /** @var string */
143 4
        $symlinkSource = $applicationParams->symlinkSource($fakeKey);
144
145
        /** @var string */
146 4
        $fakeContent = 'content';
147
148
        try {
149 4
            return $this->createDirectories($projectHomedir, $symlinkTarget)
150 4
                && $this->createFiles($snapshot, $fakeContent, $symlinkTarget, $symlinkSource)
151 2
                && $this->removeFilesAndDirs($projectHomedir, $symlinkTarget, $snapshot);
152 2
        } catch (Exception $exception) {
153 2
            $this->logger->critical('Filesystem error: ' . $exception->getMessage());
154
155 2
            return false;
156
        }
157
    }
158
159 1
    private function hasProperAccess() : bool
160
    {
161 1
        return $this->validateTicketTrackingPlatform()
162 1
            && $this->validateGitPlatform()
163 1
            && $this->validateFilesystem();
164
    }
165
166
    /**
167
     * Combines jira rest api url with jql.
168
     */
169 4
    private function createUrl(string $jql) : string
170
    {
171 4
        return $this->httpClient->applicationParams()->jiraHost()
172 4
            . '/rest/api/2/search?jql='
173 4
            . $jql
174 4
            . '&maxResults='
175 4
            . $this->httpClient->applicationParams()->jiraSearchMaxResults();
176
    }
177
178
    /**
179
     * Check wether user has permissions to create necessary directories.
180
     * Returns true if yes, false otherwise.
181
     */
182 4
    private function createDirectories(string $projectHomedir, string $symlinkTarget) : bool
183
    {
184 4
        return $this->fileManager->createDir($projectHomedir)
185 4
            && $this->fileManager->createDir($symlinkTarget);
186
    }
187
188
    /**
189
     * Check wether user has permissions to create necessary files.
190
     * Tries to write to and read from file.
191
     * Returns true if yes, false otherwise.
192
     *
193
     * @throws IOException
194
     * @throws FileNotFoundException
195
     */
196 4
    private function createFiles(
197
        string $snapshot,
198
        string $fakeContent,
199
        string $symlinkTarget,
200
        string $symlinkSource
201
    ) : bool {
202 4
        return $this->fileManager->filePutContent($snapshot, $fakeContent)
203 3
            && $this->fileManager->fileGetContent($snapshot) === $fakeContent
204 2
            && $this->fileManager->createSymlink($symlinkTarget, $symlinkSource);
205
    }
206
207
    /**
208
     * Check wether user has permissions to remove necessary directories and files.
209
     * Returns true if yes, false otherwise.
210
     *
211
     * @throws IOException
212
     */
213 2
    private function removeFilesAndDirs(string $projectHomedir, string $symlinkTarget, string $snapshot) : bool
214
    {
215 2
        $this->fileManager->remove($symlinkTarget);
216 2
        $this->fileManager->removeFile($snapshot);
217 2
        $this->fileManager->remove($projectHomedir);
218
219 2
        return !$this->fileManager->fileExists($snapshot)
220 2
            && !$this->fileManager->fileExists($projectHomedir);
221
    }
222
}
223