Files
librenms-librenms/LibreNMS/Authentication/ActiveDirectoryCommon.php
Tony Murray 2cd207028a Implement RBAC (only built in roles) (#15212)
* Install bouncer

* Seeder and level migration

* Display and edit roles

* remove unused deluser page

* Update Radius and SSO to assign roles

* update AlertUtil direct level check to use roles instead

* rewrite ircbot auth handling

* Remove legacy auth getUserlist and getUserlevel methods, add getRoles
Set roles in LegacyUserProvider

* Small cleanups

* centralize role sync code
show roles on user preferences page

* VueSelect component WIP and a little docs

* WIP

* SelectControllers id and text fields.

* LibrenmsSelect component extracted from SettingSelectDynamic

* Handle multiple selections

* allow type coercion

* full width settings

* final style adjustments

* Final compiled assets update

* Style fixes

* Fix SSO tests

* Lint cleanups

* small style fix

* don't use json yet

* Update baseline for usptream package issues

* Change schema, not 100% sure it is correct
not sure why xor doesn't work
2023-08-28 00:13:40 -05:00

224 lines
6.4 KiB
PHP

<?php
/**
* ActiveDirectoryCommonirectoryCommon.php
*
* Common code from AD auth modules
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* @link https://www.librenms.org
*
* @copyright 2018 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
namespace LibreNMS\Authentication;
use LibreNMS\Config;
trait ActiveDirectoryCommon
{
protected function getUseridFromSid($sid)
{
return preg_replace('/.*-(\d+)$/', '$1', $sid);
}
protected function sidFromLdap($sid)
{
$sidUnpacked = unpack('H*hex', $sid);
$sidHex = array_shift($sidUnpacked);
$subAuths = unpack('H2/H2/n/N/V*', $sid);
if (PHP_INT_SIZE <= 4) {
for ($i = 1; $i <= count($subAuths); $i++) {
if ($subAuths[$i] < 0) {
$subAuths[$i] = $subAuths[$i] + 0x100000000;
}
}
}
$revLevel = hexdec(substr($sidHex, 0, 2));
$authIdent = hexdec(substr($sidHex, 4, 12));
return 'S-' . $revLevel . '-' . $authIdent . '-' . implode('-', $subAuths);
}
protected function getCn($dn)
{
$dn = str_replace('\\,', '~C0mmA~', $dn);
preg_match('/[^,]*/', $dn, $matches, PREG_OFFSET_CAPTURE, 3);
return str_replace('~C0mmA~', ',', $matches[0][0]);
}
protected function getDn($samaccountname)
{
$link_identifier = $this->getConnection();
$attributes = ['dn'];
$result = ldap_search(
$link_identifier,
Config::get('auth_ad_base_dn'),
$this->groupFilter($samaccountname),
$attributes
);
$entries = ldap_get_entries($link_identifier, $result);
if ((int) $entries['count'] > 0) {
return $entries[0]['dn'];
} else {
return '';
}
}
protected function userFilter($username)
{
// don't return disabled users
$user_filter = "(&(samaccountname=$username)(!(useraccountcontrol:1.2.840.113556.1.4.803:=2))";
$extra = Config::get('auth_ad_user_filter');
if ($extra) {
$user_filter .= $extra;
}
$user_filter .= ')';
return $user_filter;
}
protected function groupFilter($groupname)
{
$group_filter = "(samaccountname=$groupname)";
$extra = Config::get('auth_ad_group_filter');
if ($extra) {
$group_filter = "(&$extra$group_filter)";
}
return $group_filter;
}
protected function getFullname($username)
{
$connection = $this->getConnection();
$attributes = ['name'];
$result = ldap_search(
$connection,
Config::get('auth_ad_base_dn'),
$this->userFilter($username),
$attributes
);
$entries = ldap_get_entries($connection, $result);
if ((int) $entries['count'] > 0) {
$membername = $entries[0]['name'][0];
} else {
$membername = $username;
}
return $membername;
}
public function getGroupList()
{
$ldap_groups = [];
// show all Active Directory Users by default
$default_group = 'Users';
if (Config::has('auth_ad_group')) {
if (Config::get('auth_ad_group') !== $default_group) {
$ldap_groups[] = Config::get('auth_ad_group');
}
}
if (! Config::has('auth_ad_groups') && ! Config::has('auth_ad_group')) {
$ldap_groups[] = $this->getDn($default_group);
}
foreach (Config::get('auth_ad_groups') as $key => $value) {
$ldap_groups[] = $this->getDn($key);
}
return $ldap_groups;
}
/**
* Generate a user array from an AD LDAP entry
* Must have the attributes: objectsid, samaccountname, displayname, mail
*
* @internal
*
* @param array $entry
* @return array
*/
protected function userFromAd($entry)
{
return [
'user_id' => $this->getUseridFromSid($this->sidFromLdap($entry['objectsid'][0])),
'username' => $entry['samaccountname'][0],
'realname' => $entry['displayname'][0],
'email' => isset($entry['mail'][0]) ? $entry['mail'][0] : null,
'descr' => '',
'can_modify_passwd' => 0,
];
}
public function getUser($user_id): array
{
$connection = $this->getConnection();
$domain_sid = $this->getDomainSid();
$search_filter = "(&(objectcategory=person)(objectclass=user)(objectsid=$domain_sid-$user_id))";
$attributes = ['samaccountname', 'displayname', 'objectsid', 'mail'];
$search = ldap_search($connection, Config::get('auth_ad_base_dn'), $search_filter, $attributes);
if ($search !== false) {
$entry = ldap_get_entries($connection, $search);
if (isset($entry[0]['samaccountname'][0])) {
return $this->userFromAd($entry[0]);
}
}
return [];
}
protected function getDomainSid(): string
{
$connection = $this->getConnection();
// Extract only the domain components
$dn_candidate = preg_replace('/^.*?DC=/i', 'DC=', Config::get('auth_ad_base_dn'));
$search = ldap_read(
$connection,
$dn_candidate,
'(objectClass=*)',
['objectsid']
);
if ($search === false) {
\Log::debug('AD Auth: Could not determine domain SID');
return '';
}
$entry = ldap_get_entries($connection, $search);
return substr($this->sidFromLdap($entry[0]['objectsid'][0]), 0, 41);
}
/**
* Provide a connected and bound ldap connection resource
*
* @return resource
*/
abstract protected function getConnection();
}