-
Notifications
You must be signed in to change notification settings - Fork 292
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add SMTP email sending, LDAP authentication with auto user creation
additional options are only visible when they are selected Signed-off-by: Aisha Tammy <[email protected]>
- Loading branch information
Showing
8 changed files
with
476 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Baikal\Core; | ||
|
||
use \Sabre\DAV; | ||
use \Sabre\VObject\ITip; | ||
|
||
/** | ||
* iMIP handler using Pear SMTP. | ||
* | ||
* This class is responsible for sending out iMIP messages. iMIP is the | ||
* email-based transport for iTIP. iTIP deals with scheduling operations for | ||
* iCalendar objects. | ||
* | ||
* If you want to customize the email that gets sent out, you can do so by | ||
* extending this class and overriding the sendMessage method. | ||
* | ||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/) | ||
* @author Aisha Tammy <[email protected]> | ||
* @license http://sabre.io/license/ Modified BSD License | ||
*/ | ||
class IMipSMTPPlugin extends \Sabre\DAV\ServerPlugin | ||
{ | ||
/** | ||
* Email address used in From: header. | ||
* | ||
* @var string | ||
*/ | ||
protected $senderEmail; | ||
|
||
/** | ||
* SMTP connection made by PEAR | ||
* | ||
*/ | ||
protected $smtp; | ||
|
||
/** | ||
* ITipMessage. | ||
* | ||
* @var ITip\Message | ||
*/ | ||
protected $itipMessage; | ||
|
||
/** | ||
* Creates the email handler. | ||
* | ||
* @param string $senderEmail. The 'senderEmail' is the email that shows up | ||
* in the 'From:' address. This should | ||
* generally be some kind of no-reply email | ||
* address you own. | ||
*/ | ||
public function __construct($senderEmail, $smtp_host = "", $smtp_port = "", $smtp_username = "", $smtp_password = "") | ||
{ | ||
require_once "Mail.php"; | ||
$this->senderEmail = $senderEmail; | ||
$this->smtp = \Mail::factory('smtp', array ('host' => $smtp_host, 'port' => $smtp_port, 'auth' => true, 'username' => $smtp_username, 'password' => $smtp_password)); | ||
} | ||
|
||
/* | ||
* This initializes the plugin. | ||
* | ||
* This function is called by Sabre\DAV\Server, after | ||
* addPlugin is called. | ||
* | ||
* This method should set up the required event subscriptions. | ||
* | ||
* @param DAV\Server $server | ||
* @return void | ||
*/ | ||
public function initialize(DAV\Server $server) | ||
{ | ||
$server->on('schedule', [$this, 'schedule'], 120); | ||
} | ||
|
||
/** | ||
* Returns a plugin name. | ||
* | ||
* Using this name other plugins will be able to access other plugins | ||
* using \Sabre\DAV\Server::getPlugin | ||
* | ||
* @return string | ||
*/ | ||
public function getPluginName() | ||
{ | ||
return 'imip-smtp'; | ||
} | ||
|
||
/** | ||
* Event handler for the 'schedule' event. | ||
*/ | ||
public function schedule(ITip\Message $iTipMessage) | ||
{ | ||
// Not sending any emails if the system considers the update | ||
// insignificant. | ||
if (!$iTipMessage->significantChange) { | ||
if (!$iTipMessage->scheduleStatus) { | ||
$iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email'; | ||
} | ||
|
||
return; | ||
} | ||
|
||
$summary = $iTipMessage->message->VEVENT->SUMMARY; | ||
|
||
if ('mailto' !== parse_url($iTipMessage->sender, PHP_URL_SCHEME)) { | ||
return; | ||
} | ||
|
||
if ('mailto' !== parse_url($iTipMessage->recipient, PHP_URL_SCHEME)) { | ||
return; | ||
} | ||
|
||
$sender = substr($iTipMessage->sender, 7); | ||
$recipient = substr($iTipMessage->recipient, 7); | ||
|
||
if ($iTipMessage->senderName) { | ||
$sender = $iTipMessage->senderName.' <'.$sender.'>'; | ||
} | ||
if ($iTipMessage->recipientName && $iTipMessage->recipientName != $recipient) { | ||
$recipient = $iTipMessage->recipientName.' <'.$recipient.'>'; | ||
} | ||
|
||
$subject = 'SabreDAV iTIP message'; | ||
switch (strtoupper($iTipMessage->method)) { | ||
case 'REPLY': | ||
$subject = 'Re: '.$summary; | ||
break; | ||
case 'REQUEST': | ||
$subject = 'Invitation: '.$summary; | ||
break; | ||
case 'CANCEL': | ||
$subject = 'Cancelled: '.$summary; | ||
break; | ||
} | ||
|
||
$headers = array( | ||
'Reply-To' => $sender, | ||
'From' => $iTipMessage->senderName.' <'.$this->senderEmail.'>', | ||
'To' => $recipient, | ||
'Subject' => $subject, | ||
'MIME-Version' => '1.0', | ||
'Content-Type' => 'text/calendar; charset=UTF-8; method='.$iTipMessage->method, | ||
); | ||
if (DAV\Server::$exposeVersion) { | ||
$headers += ['X-Sabre-Version' => DAV\Version::VERSION]; | ||
} | ||
$this->mail( | ||
$recipient, | ||
$headers, | ||
$iTipMessage->message->serialize() | ||
); | ||
$iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip'; | ||
} | ||
|
||
// @codeCoverageIgnoreStart | ||
// This is deemed untestable in a reasonable manner | ||
|
||
/** | ||
* This function is responsible for sending the actual email. | ||
* | ||
* @param string $to Recipient email address | ||
* @param string $body iCalendar body | ||
* @param array $headers List of headers | ||
*/ | ||
protected function mail($to, array $headers, $body) | ||
{ | ||
$mail = $this->smtp->send($to, $headers, $body); | ||
if (\PEAR::isError($mail)) | ||
error_log($mail->getMessage()); | ||
else | ||
error_log("Email successfully sent!"); | ||
} | ||
|
||
// @codeCoverageIgnoreEnd | ||
|
||
/** | ||
* Returns a bunch of meta-data about the plugin. | ||
* | ||
* Providing this information is optional, and is mainly displayed by the | ||
* Browser plugin. | ||
* | ||
* The description key in the returned array may contain html and will not | ||
* be sanitized. | ||
* | ||
* @return array | ||
*/ | ||
public function getPluginInfo() | ||
{ | ||
return [ | ||
'name' => $this->getPluginName(), | ||
'description' => 'Email delivery (rfc6047) for CalDAV scheduling using Pear SMTP', | ||
'link' => 'http://sabre.io/dav/scheduling/', | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
<?php | ||
|
||
namespace Baikal\Core; | ||
|
||
/** | ||
* This is an authentication backend that uses ldap. | ||
* | ||
* @copyright Copyright (C) fruux GmbH (https://fruux.com/) | ||
* @author Aisha Tammy <[email protected]> | ||
* @license http://sabre.io/license/ Modified BSD License | ||
*/ | ||
class LDAP extends \Sabre\DAV\Auth\Backend\AbstractBasic | ||
{ | ||
/** | ||
* Reference to PDO connection. | ||
* | ||
* @var PDO | ||
*/ | ||
protected $pdo; | ||
|
||
/** | ||
* PDO table name we'll be using. | ||
* | ||
* @var string | ||
*/ | ||
protected $table_name; | ||
|
||
/** | ||
* LDAP server uri. | ||
* e.g. ldaps://ldap.example.org | ||
* | ||
* @var string | ||
*/ | ||
protected $ldap_uri; | ||
|
||
/* | ||
* LDAP dn pattern for binding | ||
* | ||
* %u - gets replaced by full username | ||
* %U - gets replaced by user part when the | ||
* username is an email address | ||
* %d - gets replaced by domain part when the | ||
* username is an email address | ||
* %1-9 - gets replaced by parts of the the domain | ||
* split by '.' in reverse order | ||
* mail.example.org: %1 = org, %2 = example, %3 = mail | ||
* | ||
* @var string | ||
*/ | ||
protected $ldap_dn; | ||
|
||
/* | ||
* LDAP attribute to use for name | ||
* | ||
* @var string | ||
*/ | ||
protected $ldap_cn; | ||
|
||
/* | ||
* LDAP attribute used for mail | ||
* | ||
* @var string | ||
*/ | ||
protected $ldap_mail; | ||
|
||
/** | ||
* Creates the backend object. | ||
* | ||
* @param string $ldap_uri | ||
* @param string $ldap_dn | ||
* @param string $ldap_cn | ||
* @param string $ldap_mail | ||
* | ||
*/ | ||
public function __construct(\PDO $pdo, $table_name = 'users', $ldap_uri = 'ldap://127.0.0.1', $ldap_dn = 'mail=%u', $ldap_cn = 'cn', $ldap_mail = 'mail') | ||
{ | ||
$this->pdo = $pdo; | ||
$this->table_name = $table_name; | ||
$this->ldap_uri = $ldap_uri; | ||
$this->ldap_dn = $ldap_dn; | ||
$this->ldap_cn = $ldap_cn; | ||
$this->ldap_mail = $ldap_mail; | ||
} | ||
|
||
/** | ||
* Connects to an LDAP server and tries to authenticate. | ||
* | ||
* @param string $username | ||
* @param string $password | ||
* | ||
* @return bool | ||
*/ | ||
protected function ldapOpen($username, $password) | ||
{ | ||
$conn = ldap_connect($this->ldap_uri); | ||
if(!$conn) | ||
return false; | ||
if(!ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, 3)) | ||
return false; | ||
|
||
$success = false; | ||
|
||
$user_split = explode('@', $username, 2); | ||
$ldap_user = $user_split[0]; | ||
$ldap_domain = ''; | ||
if (count($user_split) > 1) | ||
$ldap_domain = $user_split[1]; | ||
$domain_split = array_reverse(explode('.', $ldap_domain)); | ||
|
||
$dn = str_replace('%u', $username, $this->ldap_dn); | ||
$dn = str_replace('%U', $ldap_user, $dn); | ||
$dn = str_replace('%d', $ldap_domain, $dn); | ||
for($i = 1; $i <= count($domain_split) and $i <= 9; $i++) | ||
$dn = str_replace('%' . $i, $domain_split[$i - 1], $dn); | ||
|
||
try { | ||
$bind = ldap_bind($conn, $dn, $password); | ||
if ($bind) { | ||
$success = true; | ||
} | ||
} catch (\ErrorException $e) { | ||
error_log($e->getMessage()); | ||
error_log(ldap_error($conn)); | ||
} | ||
|
||
if($success){ | ||
$stmt = $this->pdo->prepare('SELECT username, digesta1 FROM ' . $this->table_name . ' WHERE username = ?'); | ||
$stmt->execute([$username]); | ||
$result = $stmt->fetchAll(); | ||
|
||
if (empty($result)) { | ||
$search_results = ldap_read($conn, $dn, '(objectclass=*)', array($this->ldap_cn, $this->ldap_mail)); | ||
$entry = ldap_get_entries($conn, $search_results); | ||
$user_displayname = $username; | ||
$user_email = 'unset-email'; | ||
if (!empty($entry[0][$this->ldap_cn])) | ||
$user_displayname = $entry[0][$this->ldap_cn][0]; | ||
if (!empty($entry[0][$this->ldap_mail])) | ||
$user_email = $entry[0][$this->ldap_mail][0]; | ||
|
||
$user = new \Baikal\Model\User(); | ||
$user->set('username', $username); | ||
$user->set('displayname', $user_displayname); | ||
$user->set('email', $user_email); | ||
$user->persist(); | ||
} | ||
} | ||
|
||
ldap_close($conn); | ||
|
||
return $success; | ||
} | ||
|
||
/** | ||
* Validates a username and password by trying to authenticate against LDAP. | ||
* | ||
* @param string $username | ||
* @param string $password | ||
* | ||
* @return bool | ||
*/ | ||
protected function validateUserPass($username, $password) | ||
{ | ||
return $this->ldapOpen($username, $password); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.