]> BookStack Code Mirror - bookstack/blob - app/Services/LdapService.php
Got LDAP auth working to a functional state
[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']);
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         ];
36     }
37
38     /**
39      * @param Authenticatable $user
40      * @param string          $username
41      * @param string          $password
42      * @return bool
43      * @throws LdapException
44      */
45     public function validateUserCredentials(Authenticatable $user, $username, $password)
46     {
47         $ldapUser = $this->getUserDetails($username);
48         if ($ldapUser === null) return false;
49         if ($ldapUser['uid'] !== $user->external_auth_id) return false;
50
51         $ldapConnection = $this->getConnection();
52         $ldapBind = @ldap_bind($ldapConnection, $ldapUser['dn'], $password);
53         return $ldapBind;
54     }
55
56     /**
57      * Bind the system user to the LDAP connection using the given credentials
58      * otherwise anonymous access is attempted.
59      * @param $connection
60      * @throws LdapException
61      */
62     protected function bindSystemUser($connection)
63     {
64         $ldapDn = config('services.ldap.dn');
65         $ldapPass = config('services.ldap.pass');
66
67         $isAnonymous = ($ldapDn === false || $ldapPass === false);
68         if ($isAnonymous) {
69             $ldapBind = ldap_bind($connection);
70         } else {
71             $ldapBind = ldap_bind($connection, $ldapDn, $ldapPass);
72         }
73
74         if (!$ldapBind) throw new LdapException('LDAP access failed using ' . $isAnonymous ? ' anonymous bind.' : ' given dn & pass details');
75     }
76
77     /**
78      * Get the connection to the LDAP server.
79      * Creates a new connection if one does not exist.
80      * @return resource
81      * @throws LdapException
82      */
83     protected function getConnection()
84     {
85         if ($this->ldapConnection !== null) return $this->ldapConnection;
86
87         // Check LDAP extension in installed
88         if (!function_exists('ldap_connect')) {
89             throw new LdapException('LDAP PHP extension not installed');
90         }
91
92         // Get port from server string if specified.
93         $ldapServer = explode(':', config('services.ldap.server'));
94         $ldapConnection = ldap_connect($ldapServer[0], count($ldapServer) > 1 ? $ldapServer[1] : 389);
95
96         if ($ldapConnection === false) {
97             throw new LdapException('Cannot connect to ldap server, Initial connection failed');
98         }
99
100         // Set any required options
101         ldap_set_option($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3); // TODO - make configurable
102
103         $this->ldapConnection = $ldapConnection;
104         return $this->ldapConnection;
105     }
106
107     /**
108      * Build a filter string by injecting common variables.
109      * @param       $filterString
110      * @param array $attrs
111      * @return string
112      */
113     protected function buildFilter($filterString, array $attrs)
114     {
115         $newAttrs = [];
116         foreach ($attrs as $key => $attrText) {
117             $newKey = '${' . $key . '}';
118             $newAttrs[$newKey] = $attrText;
119         }
120         return strtr($filterString, $newAttrs);
121     }
122
123 }