r2361 - worked on ticket:186 (checkboxes in tag lists)
[racktables] / inc / auth.php
CommitLineData
b325120a 1<?php
e673ee24
DO
2/*
3
4Authentication library for RackTables.
5
6*/
7
8// This function ensures that we don't continue without a legitimate
d4d873be
DO
9// username and password (also make sure, that both are present, this
10// is especially useful for LDAP auth code to not deceive itself with
204284ba
DO
11// anonymous binding). It also initializes $remote_username and $accounts.
12// Fatal errors are followed by exit (1) to aid in script debugging.
e673ee24
DO
13function authenticate ()
14{
204284ba
DO
15 global $remote_username, $accounts, $user_auth_src, $require_valid_user, $script_mode;
16 if (!isset ($user_auth_src) or !isset ($require_valid_user))
17 {
18 showError ('secret.php misconfiguration: either user_auth_src or require_valid_user are missing', __FUNCTION__);
19 exit (1);
20 }
21 $accounts = getUserAccounts();
22 if ($accounts === NULL)
23 {
24 showError ('Failed to initialize access database.', __FUNCTION__);
25 exit (1);
26 }
27 if (isset ($script_mode) and $script_mode === TRUE)
28 return;
dc9ea133 29 if (isset ($_REQUEST['logout']))
204284ba
DO
30 dieWith401(); // Reset browser credentials cache.
31 switch ($user_auth_src)
e673ee24 32 {
dc9ea133
DO
33 case 'database':
34 case 'ldap':
35 if
36 (
37 !isset ($_SERVER['PHP_AUTH_USER']) or
38 !strlen ($_SERVER['PHP_AUTH_USER']) or
39 !isset ($_SERVER['PHP_AUTH_PW']) or
40 !strlen ($_SERVER['PHP_AUTH_PW'])
41 )
42 dieWith401();
43 $remote_username = $_SERVER['PHP_AUTH_USER'];
44 break;
45 case 'httpd':
46 if
47 (
48 !isset ($_SERVER['REMOTE_USER']) or
49 !strlen ($_SERVER['REMOTE_USER'])
50 )
51 {
52 showError ('System misconfiguration. The web-server didn\'t authenticate the user, although ought to do.');
53 die;
54 }
55 $remote_username = $_SERVER['REMOTE_USER'];
56 break;
57 default:
58 showError ('Invalid authentication source!', __FUNCTION__);
59 die;
e673ee24 60 }
dc9ea133
DO
61 if ($require_valid_user and !isset ($accounts[$remote_username]))
62 dieWith401();
63 if (isset ($accounts[$remote_username]) and $accounts[$remote_username]['user_enabled'] != 'yes')
64 dieWith401();
65 switch (TRUE)
66 {
67 // Just trust the server, because the password isn't known.
204284ba 68 case ('httpd' == $user_auth_src):
dc9ea133
DO
69 if (authenticated_via_httpd ($remote_username))
70 return;
71 break;
72 // When using LDAP, leave a mean to fix things. Admin user is always authenticated locally.
204284ba 73 case ('database' == $user_auth_src or $accounts[$remote_username]['user_id'] == 1):
dc9ea133
DO
74 if (authenticated_via_database ($remote_username, $_SERVER['PHP_AUTH_PW']))
75 return;
76 break;
204284ba 77 case ('ldap' == $user_auth_src):
dc9ea133
DO
78 if (authenticated_via_ldap ($remote_username, $_SERVER['PHP_AUTH_PW']))
79 return;
80 break;
81 default:
82 showError ('Invalid authentication source!', __FUNCTION__);
83 die;
84 }
85 dieWith401();
86}
87
88function dieWith401 ()
89{
90 header ('WWW-Authenticate: Basic realm="' . getConfigVar ('enterprise') . ' RackTables access"');
91 header ('HTTP/1.0 401 Unauthorized');
92 showError ('This system requires authentication. You should use a username and a password.');
93 die();
e673ee24
DO
94}
95
da958e52
DO
96// Merge accumulated tags into a single chain, add location-specific
97// autotags and try getting access clearance. Page and tab are mandatory,
98// operation is optional.
46f92ff7 99function permitted ($p = NULL, $t = NULL, $o = NULL, $annex = array())
e673ee24 100{
da958e52
DO
101 global $pageno, $tabno, $op;
102 global
103 $user_tags,
104 $auto_tags,
105 $expl_tags,
106 $impl_tags;
107
108 if ($p === NULL)
109 $p = $pageno;
110 if ($t === NULL)
111 $t = $tabno;
112 $subject = array_merge
113 (
114 $user_tags,
115 $auto_tags,
116 $expl_tags,
46f92ff7
DO
117 $impl_tags,
118 $annex
da958e52
DO
119 );
120 $subject[] = array ('tag' => '$page_' . $p);
121 $subject[] = array ('tag' => '$tab_' . $t);
122 if ($o === NULL and isset ($op))
7a4fcf70 123 {
da958e52 124 $subject[] = array ('tag' => '$op_' . $op);
7a4fcf70
DO
125 $subject[] = array ('tag' => '$any_op');
126 }
1c9621a7
DO
127 // XXX: The solution below is only appropriate for a corner case of a more universal
128 // problem: to make the decision for an entity belonging to a cascade of nested
129 // containers. Each container being an entity itself, it may have own tags (explicit
130 // and implicit accordingly). There's a fixed set of rules (RackCode) with each rule
131 // being able to evaluate any built and given context and produce either a decision
132 // or a lack of decision.
133 // There are several levels of context for the target entity, at least one for entities
134 // belonging directly to the tree root. Each level's context is a union of given
135 // container's tags and the tags of the contained entities.
136 // The universal problem originates from the fact, that certain rules may change
137 // their product as context level changes, thus forcing some final decision (but not
138 // adding a lack of it). With rule code being principles and context cascade being
139 // circumstances, there are two uttermost approaches or moralities.
140 //
141 // Fundamentalism: principles over circumstances. When a rule doesn't produce any
142 // decision, go on to the next rule. When all rules are evaluated, go on to the next
143 // security context level.
144 //
145 // Opportunism: circumstances over principles. With a lack of decision, work with the
146 // same rule, trying to evaluate it against the next level (and next, and next...),
147 // until all levels are tried. Only then go on to the next rule.
148 //
149 // With the above being simple discrete algorythms, I believe, that they very reliably
150 // replicate human behavior. This gives a vast ground for further research, so I would
151 // only note, that the morale used in RackTables is "principles first".
da958e52 152 return gotClearanceForTagChain ($subject);
e673ee24
DO
153}
154
810e3422 155function accessibleSubpage ($p)
b9bd9897 156{
da958e52
DO
157 global $user_tags;
158 $subject = $user_tags;
159 $subject[] = array ('tag' => '$page_' . $p);
810e3422 160 $subject[] = array ('tag' => '$tab_default');
da958e52 161 return gotClearanceForTagChain ($subject);
b9bd9897
DO
162}
163
7dfd5e44
DO
164function authenticated_via_ldap ($username, $password)
165{
8c3bd904 166 global $ldap_server, $ldap_domain, $ldap_search_dn, $ldap_search_attr;
ae65938e 167 if ($connect = @ldap_connect ($ldap_server))
8c3bd904
DO
168 {
169 if
170 (
171 !isset ($ldap_search_dn) or
172 !isset ($ldap_search_attr) or
173 empty ($ldap_search_dn) or
174 empty ($ldap_search_attr)
175 )
176 $user_name = $username . "@" . $ldap_domain;
177 else
178 {
179 $results = @ldap_search ($connect, $ldap_search_dn, "(${ldap_search_attr}=${username})", array("dn"));
180 if (@ldap_count_entries ($connect, $results) != 1)
181 {
182 @ldap_close ($connect);
183 return FALSE;
184 }
185 $info = @ldap_get_entries($connect,$results);
186 $user_name = $info[0]['dn'];
187 }
188 if ($bind = @ldap_bind ($connect, $user_name, $password))
ae65938e
DO
189 {
190 @ldap_close ($connect);
191 return TRUE;
192 }
8c3bd904 193 }
ae65938e 194 @ldap_close ($connect);
7dfd5e44
DO
195 return FALSE;
196}
197
198function authenticated_via_database ($username, $password)
199{
200 global $accounts;
201 if (!defined ('HASH_HMAC'))
202 {
203 showError ('Fatal error: PHP hash extension is missing', __FUNCTION__);
204 die();
205 }
206 if (array_search (PASSWORD_HASH, hash_algos()) === FALSE)
207 {
208 showError ('Password hash not supported, authentication impossible.', __FUNCTION__);
209 die();
210 }
211 if (!isset ($accounts[$username]['user_password_hash']))
212 return FALSE;
e673ee24
DO
213 if ($accounts[$username]['user_password_hash'] == hash (PASSWORD_HASH, $password))
214 return TRUE;
215 return FALSE;
216}
217
dc9ea133
DO
218function authenticated_via_httpd ($username)
219{
220 // Reaching here means, that .htaccess authentication passed.
221 // Let's make sure, that user exists in the database, and give clearance.
222 global $accounts;
223 return isset ($accounts[$username]);
224}
225
e673ee24
DO
226// This function returns password hash for given user ID.
227function getHashByID ($user_id = 0)
228{
229 if ($user_id <= 0)
230 {
b09549b3 231 showError ('Invalid user_id', __FUNCTION__);
e673ee24
DO
232 return NULL;
233 }
234 global $accounts;
235 foreach ($accounts as $account)
236 if ($account['user_id'] == $user_id)
237 return $account['user_password_hash'];
238 return NULL;
239}
240
b9bd9897
DO
241// Likewise.
242function getUsernameByID ($user_id = 0)
243{
244 if ($user_id <= 0)
245 {
246 showError ('Invalid user_id', __FUNCTION__);
247 return NULL;
248 }
249 global $accounts;
250 foreach ($accounts as $account)
251 if ($account['user_id'] == $user_id)
252 return $account['user_name'];
253 showError ("User with ID '${user_id}' not found!");
254 return NULL;
255}
256
e673ee24 257?>