]> BookStack Code Mirror - bookstack/blob - app/Services/LdapService.php
cd80290e48b39a1c90b718c2b4e884b6a1f23686
[bookstack] / app / Services / LdapService.php
1 <?php namespace BookStack\Services;
2
3
4 use BookStack\Exceptions\LdapException;
5 use Illuminate\Contracts\Auth\Authenticatable;
6
7 class LdapService
8 {
9
10     protected $ldapConnection;
11
12     /**
13      * Get the details of a user from LDAP using the given username.
14      * User found via configurable user filter.
15      * @param $userName
16      * @return array|null
17      * @throws LdapException
18      */
19     public function getUserDetails($userName)
20     {
21         $ldapConnection = $this->getConnection();
22
23         // Find user
24         $userFilter = $this->buildFilter(config('services.ldap.user_filter'), ['user' => $userName]);
25         $baseDn = config('services.ldap.base_dn');
26         $ldapSearch = ldap_search($ldapConnection, $baseDn, $userFilter, ['cn', 'uid', 'dn', 'mail']);
27         $users = ldap_get_entries($ldapConnection, $ldapSearch);
28         if ($users['count'] === 0) return null;
29
30         $user = $users[0];
31         return [
32             'uid'  => $user['uid'][0],
33             'name' => $user['cn'][0],
34             'dn'   => $user['dn'],
35             'email' => (isset($user['mail'])) ? $user['mail'][0] : null
36         ];
37     }
38
39     /**
40      * @param Authenticatable $user
41      * @param string          $username
42      * @param string          $password
43      * @return bool
44      * @throws LdapException
45      */
46     public function validateUserCredentials(Authenticatable $user, $username, $password)
47     {
48         $ldapUser = $this->getUserDetails($username);
49         if ($ldapUser === null) return false;
50         if ($ldapUser['uid'] !== $user->external_auth_id) return false;
51
52         $ldapConnection = $this->getConnection();
53         $ldapBind = @ldap_bind($ldapConnection, $ldapUser['dn'], $password);
54         return $ldapBind;
55     }
56
57     /**
58      * Bind the system user to the LDAP connection using the given credentials
59      * otherwise anonymous access is attempted.
60      * @param $connection
61      * @throws LdapException
62      */
63     protected function bindSystemUser($connection)
64     {
65         $ldapDn = config('services.ldap.dn');
66         $ldapPass = config('services.ldap.pass');
67
68         $isAnonymous = ($ldapDn === false || $ldapPass === false);
69         if ($isAnonymous) {
70             $ldapBind = ldap_bind($connection);
71         } else {
72             $ldapBind = ldap_bind($connection, $ldapDn, $ldapPass);
73         }
74
75         if (!$ldapBind) throw new LdapException('LDAP access failed using ' . $isAnonymous ? ' anonymous bind.' : ' given dn & pass details');
76     }
77
78     /**
79      * Get the connection to the LDAP server.
80      * Creates a new connection if one does not exist.
81      * @return resource
82      * @throws LdapException
83      */
84     protected function getConnection()
85     {
86         if ($this->ldapConnection !== null) return $this->ldapConnection;
87
88         // Check LDAP extension in installed
89         if (!function_exists('ldap_connect')) {
90             throw new LdapException('LDAP PHP extension not installed');
91         }
92
93         // Get port from server string if specified.
94         $ldapServer = explode(':', config('services.ldap.server'));
95         $ldapConnection = ldap_connect($ldapServer[0], count($ldapServer) > 1 ? $ldapServer[1] : 389);
96
97         if ($ldapConnection === false) {
98             throw new LdapException('Cannot connect to ldap server, Initial connection failed');
99         }
100
101         // Set any required options
102         ldap_set_option($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3); // TODO - make configurable
103
104         $this->ldapConnection = $ldapConnection;
105         return $this->ldapConnection;
106     }
107
108     /**
109      * Build a filter string by injecting common variables.
110      * @param       $filterString
111      * @param array $attrs
112      * @return string
113      */
114     protected function buildFilter($filterString, array $attrs)
115     {
116         $newAttrs = [];
117         foreach ($attrs as $key => $attrText) {
118             $newKey = '${' . $key . '}';
119             $newAttrs[$newKey] = $attrText;
120         }
121         return strtr($filterString, $newAttrs);
122     }
123
124 }