-
-
Notifications
You must be signed in to change notification settings - Fork 45
Expand file tree
/
Copy pathIMAPAuth.php
More file actions
158 lines (134 loc) 路 4.59 KB
/
Copy pathIMAPAuth.php
File metadata and controls
158 lines (134 loc) 路 4.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
<?php
namespace App\Services;
use App\Entity\User;
use Doctrine\Persistence\ManagerRegistry;
use Webklex\PHPIMAP\Client;
use Webklex\PHPIMAP\ClientManager;
final class IMAPAuth extends AbstractAuth
{
/**
* Doctrine registry.
*
* @var ManagerRegistry
*/
private $doctrine;
/**
* Utils class.
*
* @var Utils
*/
private $utils;
/**
* Should we auto create the user upon successful
* login if it does not exist yet.
*
* @var bool
*/
private $autoCreate;
/**
* IMAP server host.
*
* @var string
*/
private $IMAPHost;
/**
* IMAP server port.
*
* @var int
*/
private $IMAPPort;
/**
* IMAP encryption method. Could be ssl, tls or false.
*
* @var mixed (string or bool)
*/
private $IMAPEncryptionMethod;
/**
* Should we validate the certificate?
*
* @var bool
*/
private $IMAPCertificateValidation;
public function __construct(ManagerRegistry $doctrine, Utils $utils, string $IMAPAuthUrl, bool $autoCreate, string $IMAPEncryptionMethod, bool $IMAPCertificateValidation)
{
$components = parse_url($IMAPAuthUrl);
if (!$components) {
throw new Exception('IMAP Error (parsing IMAP url "'.$IMAPAuthUrl.'"): '.$e->getMessage());
}
$this->IMAPHost = $components['host'] ?? null;
// Trying to choose the best port if it was not provided,
// defaulting to 993 (secure)
if (isset($components['port'])) {
$this->IMAPPort = $components['port'];
} elseif (false === $this->IMAPEncryptionMethod) {
$this->IMAPPort = 143;
} else {
$this->IMAPPort = 993;
}
// We're making sure that only ssl, tls or 'false' are passed down to the IMAP client,
// defaulting to SSL
$IMAPEncryptionMethodCleaned = strtolower($IMAPEncryptionMethod);
if ('false' === $IMAPEncryptionMethodCleaned) {
$this->IMAPEncryptionMethod = false;
} elseif ('tls' === $IMAPEncryptionMethodCleaned) {
$this->IMAPEncryptionMethod = 'tls';
} else {
$this->IMAPEncryptionMethod = 'ssl';
}
$this->IMAPCertificateValidation = $IMAPCertificateValidation;
$this->autoCreate = $autoCreate;
$this->doctrine = $doctrine;
$this->utils = $utils;
}
/**
* Connects to an IMAP server and tries to authenticate.
* If the user does not exist, create it (depending on the autoCreate flag).
*/
protected function imapOpen(string $username, string $password): bool
{
$cm = new ClientManager($options = []);
// Create a new instance of the IMAP client manually
$client = $cm->make([
'host' => $this->IMAPHost,
'port' => $this->IMAPPort,
'encryption' => $this->IMAPEncryptionMethod,
'validate_cert' => $this->IMAPCertificateValidation,
'username' => $username,
'password' => $password,
'protocol' => 'imap',
]);
try {
$client->connect();
$client->disconnect();
$success = true;
} catch (\Exception $e) {
error_log('IMAP Error (connection): '.$e->getMessage());
$success = false;
}
// Auto-create the user if it does not already exist in the database
if ($success && $this->autoCreate) {
$user = $this->doctrine->getRepository(User::class)->findOneBy(['username' => $username]);
if (!$user) {
try {
// We only have a username, so we use it for displayname and email
$this->utils->createPasswordlessUserWithDefaultObjects($username, $username, $username);
$this->doctrine->getManager()->flush();
} catch (\Throwable $e) {
// Letting the login through without a principal would leave the account
// authenticated but unusable: no calendar home, so clients fall back to the
// server root and every write is refused.
error_log('IMAP Error (could not create the user "'.$username.'"): '.$e->getMessage());
return false;
}
}
}
return $success;
}
/**
* Validates a username and password by trying to authenticate against IMAP.
*/
protected function checkCredentials(string $username, string $password): bool
{
return $this->imapOpen($username, $password);
}
}