Changeset View
Changeset View
Standalone View
Standalone View
src/app/Backends/LDAP.php
<?php | <?php | ||||
namespace App\Backends; | namespace App\Backends; | ||||
use App\Domain; | use App\Domain; | ||||
use App\Group; | use App\Group; | ||||
use App\Resource; | use App\Resource; | ||||
use App\SharedFolder; | |||||
use App\User; | use App\User; | ||||
class LDAP | class LDAP | ||||
{ | { | ||||
/** @const array Group settings used by the backend */ | /** @const array Group settings used by the backend */ | ||||
public const GROUP_SETTINGS = [ | public const GROUP_SETTINGS = [ | ||||
'sender_policy', | 'sender_policy', | ||||
]; | ]; | ||||
/** @const array Resource settings used by the backend */ | /** @const array Resource settings used by the backend */ | ||||
public const RESOURCE_SETTINGS = [ | public const RESOURCE_SETTINGS = [ | ||||
'folder', | 'folder', | ||||
'invitation_policy', | 'invitation_policy', | ||||
]; | ]; | ||||
/** @const array Shared folder settings used by the backend */ | |||||
public const SHARED_FOLDER_SETTINGS = [ | |||||
'folder', | |||||
'acl', | |||||
]; | |||||
/** @const array User settings used by the backend */ | /** @const array User settings used by the backend */ | ||||
public const USER_SETTINGS = [ | public const USER_SETTINGS = [ | ||||
'first_name', | 'first_name', | ||||
'last_name', | 'last_name', | ||||
'organization', | 'organization', | ||||
]; | ]; | ||||
/** @var ?\Net_LDAP3 LDAP connection object */ | /** @var ?\Net_LDAP3 LDAP connection object */ | ||||
▲ Show 20 Lines • Show All 261 Lines • ▼ Show 20 Lines | public static function createResource(Resource $resource): void | ||||
); | ); | ||||
if (empty(self::$ldap)) { | if (empty(self::$ldap)) { | ||||
$ldap->close(); | $ldap->close(); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Create a shared folder in LDAP. | |||||
* | |||||
* @param \App\SharedFolder $folder The shared folder to create. | |||||
* | |||||
* @throws \Exception | |||||
*/ | |||||
public static function createSharedFolder(SharedFolder $folder): void | |||||
{ | |||||
$config = self::getConfig('admin'); | |||||
$ldap = self::initLDAP($config); | |||||
$domainName = explode('@', $folder->email, 2)[1]; | |||||
$cn = $ldap->quote_string($folder->name); | |||||
$dn = "cn={$cn}," . self::baseDN($domainName, 'Shared Folders'); | |||||
$entry = [ | |||||
'mail' => $folder->email, | |||||
'objectclass' => [ | |||||
'top', | |||||
'kolabsharedfolder', | |||||
'mailrecipient', | |||||
], | |||||
]; | |||||
self::setSharedFolderAttributes($ldap, $folder, $entry); | |||||
self::addEntry( | |||||
$ldap, | |||||
$dn, | |||||
$entry, | |||||
"Failed to create shared folder {$folder->id} in LDAP (" . __LINE__ . ")" | |||||
); | |||||
if (empty(self::$ldap)) { | |||||
$ldap->close(); | |||||
} | |||||
} | |||||
/** | |||||
* Create a user in LDAP. | * Create a user in LDAP. | ||||
* | * | ||||
* Only need to add user if in any of the local domains? Figure that out here for now. Should | * Only need to add user if in any of the local domains? Figure that out here for now. Should | ||||
* have Context-Based Access Controls before the job is queued though, probably. | * have Context-Based Access Controls before the job is queued though, probably. | ||||
* | * | ||||
* Use one of three modes; | * Use one of three modes; | ||||
* | * | ||||
* 1) The authenticated user account. | * 1) The authenticated user account. | ||||
▲ Show 20 Lines • Show All 144 Lines • ▼ Show 20 Lines | public static function deleteResource(Resource $resource): void | ||||
} | } | ||||
if (empty(self::$ldap)) { | if (empty(self::$ldap)) { | ||||
$ldap->close(); | $ldap->close(); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Delete a shared folder from LDAP. | |||||
* | |||||
* @param \App\SharedFolder $folder The shared folder to delete. | |||||
* | |||||
* @throws \Exception | |||||
*/ | |||||
public static function deleteSharedFolder(SharedFolder $folder): void | |||||
{ | |||||
$config = self::getConfig('admin'); | |||||
$ldap = self::initLDAP($config); | |||||
if (self::getSharedFolderEntry($ldap, $folder->email, $dn)) { | |||||
$result = $ldap->delete_entry($dn); | |||||
if (!$result) { | |||||
self::throwException( | |||||
$ldap, | |||||
"Failed to delete shared folder {$folder->id} from LDAP (" . __LINE__ . ")" | |||||
); | |||||
} | |||||
} | |||||
if (empty(self::$ldap)) { | |||||
$ldap->close(); | |||||
} | |||||
} | |||||
/** | |||||
* Delete a user from LDAP. | * Delete a user from LDAP. | ||||
* | * | ||||
* @param \App\User $user The user account to delete. | * @param \App\User $user The user account to delete. | ||||
* | * | ||||
* @throws \Exception | * @throws \Exception | ||||
*/ | */ | ||||
public static function deleteUser(User $user): void | public static function deleteUser(User $user): void | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 82 Lines • ▼ Show 20 Lines | public static function getResource(string $email) | ||||
if (empty(self::$ldap)) { | if (empty(self::$ldap)) { | ||||
$ldap->close(); | $ldap->close(); | ||||
} | } | ||||
return $resource; | return $resource; | ||||
} | } | ||||
/** | /** | ||||
* Get a shared folder data from LDAP. | |||||
* | |||||
* @param string $email The resource email. | |||||
* | |||||
* @return array|false|null | |||||
* @throws \Exception | |||||
*/ | |||||
public static function getSharedFolder(string $email) | |||||
{ | |||||
$config = self::getConfig('admin'); | |||||
$ldap = self::initLDAP($config); | |||||
$folder = self::getSharedFolderEntry($ldap, $email, $dn); | |||||
if (empty(self::$ldap)) { | |||||
$ldap->close(); | |||||
} | |||||
return $folder; | |||||
} | |||||
/** | |||||
* Get a user data from LDAP. | * Get a user data from LDAP. | ||||
* | * | ||||
* @param string $email The user email. | * @param string $email The user email. | ||||
* | * | ||||
* @return array|false|null | * @return array|false|null | ||||
* @throws \Exception | * @throws \Exception | ||||
*/ | */ | ||||
public static function getUser(string $email) | public static function getUser(string $email) | ||||
▲ Show 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | public static function updateResource(Resource $resource): void | ||||
} | } | ||||
if (empty(self::$ldap)) { | if (empty(self::$ldap)) { | ||||
$ldap->close(); | $ldap->close(); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Update a shared folder in LDAP. | |||||
* | |||||
* @param \App\SharedFolder $folder The shared folder to update | |||||
* | |||||
* @throws \Exception | |||||
*/ | |||||
public static function updateSharedFolder(SharedFolder $folder): void | |||||
{ | |||||
$config = self::getConfig('admin'); | |||||
$ldap = self::initLDAP($config); | |||||
$newEntry = $oldEntry = self::getSharedFolderEntry($ldap, $folder->email, $dn); | |||||
if (empty($oldEntry)) { | |||||
self::throwException( | |||||
$ldap, | |||||
"Failed to update shared folder {$folder->id} in LDAP (folder not found)" | |||||
); | |||||
} | |||||
self::setSharedFolderAttributes($ldap, $folder, $newEntry); | |||||
$result = $ldap->modify_entry($dn, $oldEntry, $newEntry); | |||||
if (!is_array($result)) { | |||||
self::throwException( | |||||
$ldap, | |||||
"Failed to update shared folder {$folder->id} in LDAP (" . __LINE__ . ")" | |||||
); | |||||
} | |||||
if (empty(self::$ldap)) { | |||||
$ldap->close(); | |||||
} | |||||
} | |||||
/** | |||||
* Update a user in LDAP. | * Update a user in LDAP. | ||||
* | * | ||||
* @param \App\User $user The user account to update. | * @param \App\User $user The user account to update. | ||||
* | * | ||||
* @throws \Exception | * @throws \Exception | ||||
*/ | */ | ||||
public static function updateUser(User $user): void | public static function updateUser(User $user): void | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 176 Lines • ▼ Show 20 Lines | private static function setResourceAttributes($ldap, Resource $resource, &$entry) | ||||
// TODO: Set folder ACL so the owner can write to it | // TODO: Set folder ACL so the owner can write to it | ||||
// TODO: Do we need to add lrs for anyone? | // TODO: Do we need to add lrs for anyone? | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Set common shared folder attributes | |||||
*/ | |||||
private static function setSharedFolderAttributes($ldap, SharedFolder $folder, &$entry) | |||||
{ | |||||
$settings = $folder->getSettings(['acl', 'folder']); | |||||
$entry['cn'] = $folder->name; | |||||
$entry['kolabfoldertype'] = $folder->type; | |||||
$entry['kolabtargetfolder'] = $settings['folder'] ?? ''; | |||||
$entry['acl'] = !empty($settings['acl']) ? json_decode($settings['acl'], true) : ''; | |||||
} | |||||
/** | |||||
* Set common user attributes | * Set common user attributes | ||||
*/ | */ | ||||
private static function setUserAttributes(User $user, array &$entry) | private static function setUserAttributes(User $user, array &$entry) | ||||
{ | { | ||||
$settings = $user->getSettings(['first_name', 'last_name', 'organization']); | $settings = $user->getSettings(['first_name', 'last_name', 'organization']); | ||||
$firstName = $settings['first_name']; | $firstName = $settings['first_name']; | ||||
$lastName = $settings['last_name']; | $lastName = $settings['last_name']; | ||||
▲ Show 20 Lines • Show All 125 Lines • ▼ Show 20 Lines | private static function getResourceEntry($ldap, $email, &$dn = null) | ||||
// For resources we're using search() instead of get_entry() because | // For resources we're using search() instead of get_entry() because | ||||
// a resource name is not constant, so e.g. on update we might have | // a resource name is not constant, so e.g. on update we might have | ||||
// the new name, but not the old one. Email address is constant. | // the new name, but not the old one. Email address is constant. | ||||
return self::searchEntry($ldap, $base_dn, "(mail=$email)", $attrs, $dn); | return self::searchEntry($ldap, $base_dn, "(mail=$email)", $attrs, $dn); | ||||
} | } | ||||
/** | /** | ||||
* Get a shared folder entry from LDAP. | |||||
* | |||||
* @param \Net_LDAP3 $ldap Ldap connection | |||||
* @param string $email Resource email (mail) | |||||
* @param string $dn Reference to the shared folder DN | |||||
* | |||||
* @return null|array Shared folder entry, NULL if not found | |||||
*/ | |||||
private static function getSharedFolderEntry($ldap, $email, &$dn = null) | |||||
{ | |||||
$domainName = explode('@', $email, 2)[1]; | |||||
$base_dn = self::baseDN($domainName, 'Shared Folders'); | |||||
$attrs = ['dn', 'cn', 'mail', 'objectclass', 'kolabtargetfolder', 'kolabfoldertype', 'acl']; | |||||
// For shared folders we're using search() instead of get_entry() because | |||||
// a folder name is not constant, so e.g. on update we might have | |||||
// the new name, but not the old one. Email address is constant. | |||||
return self::searchEntry($ldap, $base_dn, "(mail=$email)", $attrs, $dn); | |||||
} | |||||
/** | |||||
* Get user entry from LDAP. | * Get user entry from LDAP. | ||||
* | * | ||||
* @param \Net_LDAP3 $ldap Ldap connection | * @param \Net_LDAP3 $ldap Ldap connection | ||||
* @param string $email User email (uid) | * @param string $email User email (uid) | ||||
* @param string $dn Reference to user DN | * @param string $dn Reference to user DN | ||||
* @param bool $full Get extra attributes, e.g. nsroledn | * @param bool $full Get extra attributes, e.g. nsroledn | ||||
* | * | ||||
* @return ?array User entry, NULL if not found | * @return ?array User entry, NULL if not found | ||||
▲ Show 20 Lines • Show All 173 Lines • Show Last 20 Lines |