Completed
Push — master ( 9eadf5...6ba699 )
by Andreas
16:50
created

midcom_services_auth::get_midgard_group_by_name()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 1
dl 0
loc 12
ccs 0
cts 9
cp 0
crap 6
rs 9.9666
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package midcom.services
4
 * @author The Midgard Project, http://www.midgard-project.org
5
 * @copyright The Midgard Project, http://www.midgard-project.org
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 */
8
9
use Symfony\Component\HttpFoundation\Request;
10
11
/**
12
 * Main Authentication/Authorization service class, it provides means to authenticate
13
 * users and to check for permissions.
14
 *
15
 * <b>Authentication</b>
16
 *
17
 * Whenever the system successfully creates a new login session (during auth service startup),
18
 * it checks whether the key <i>midcom_services_auth_login_success_url</i> is present in the HTTP
19
 * Request data. If this is the case, it relocates to the URL given in it. This member isn't set
20
 * by default in the MidCOM core, it is intended for custom authentication forms. The MidCOM
21
 * relocate function is used to for relocation, thus you can take full advantage of the
22
 * convenience functions in there. See midcom_application::relocate() for details.
23
 *
24
 * <b>Checking Privileges</b>
25
 *
26
 * This class offers various methods to verify the privilege state of a user, all of them prefixed
27
 * with can_* for privileges and is_* for membership checks.
28
 *
29
 * Each function is available in a simple check version, which returns true or false, and a
30
 * require_* prefixed variant, which has no return value. The require variants of these calls
31
 * instead check if the given condition is met, if yes, they return silently, otherwise they
32
 * throw an access denied error.
33
 *
34
 * @todo Fully document authentication.
35
 * @package midcom.services
36
 */
37
class midcom_services_auth
38
{
39
    /**
40
     * The currently authenticated user or null in case of anonymous access.
41
     * It is to be considered read-only.
42
     *
43
     * @var midcom_core_user
44
     */
45
    public $user;
46
47
    /**
48
     * Admin user level state. This is true if the currently authenticated user is an
49
     * Midgard Administrator, false otherwise.
50
     *
51
     * This effectively maps to midcom_connection::is_admin(); but it is suggested to use the auth class
52
     * for consistency reasons nevertheless.
53
     *
54
     * @var boolean
55
     */
56
    public $admin = false;
57
58
    /**
59
     * The ACL management system.
60
     *
61
     * @var midcom_services_auth_acl
62
     */
63
    public $acl;
64
65
    /**
66
     * Internal cache of all loaded groups, indexed by their identifiers.
67
     *
68
     * @var Array
69
     */
70
    private $_group_cache = [];
71
72
    /**
73
     * Internal cache of all loaded users, indexed by their identifiers.
74
     *
75
     * @var Array
76
     */
77
    private $_user_cache = [];
78
79
    /**
80
     * This flag indicates if sudo mode is active during execution. This will only be the
81
     * case if the sudo system actually grants this privileges, and only until components
82
     * release the rights again. This does override the full access control system at this time
83
     * and essentially give you full admin privileges (though this might change in the future).
84
     *
85
     * Note, that this is no boolean but an int, otherwise it would be impossible to trace nested
86
     * sudo invocations, which are quite possible with multiple components calling each others
87
     * callback. A value of 0 indicates that sudo is inactive. A value greater than zero indicates
88
     * sudo mode is active, with the count being equal to the depth of the sudo callers.
89
     *
90
     * It is thus still safely possible to evaluate this member in a boolean context to check
91
     * for an enabled sudo mode.
92
     *
93
     * @var int
94
     * @see request_sudo()
95
     * @see drop_sudo()
96
     */
97
    private $_component_sudo = 0;
98
99
    /**
100
     * The authentication backend we should use by default.
101
     *
102
     * @var midcom_services_auth_backend
103
     */
104
    private $backend;
105
106
    /**
107
     * The authentication frontend we should use by default.
108
     *
109
     * @var midcom_services_auth_frontend
110
     */
111
    private $frontend;
112
113
    /**
114
     * Initialize the service:
115
     *
116
     * - Start up the login session service
117
     * - Load the core privileges.
118
     * - Initialize to the Midgard Authentication, then synchronize with the auth
119
     *   drivers' currently authenticated user overriding the Midgard Auth if
120
     *   necessary.
121
     */
122 5
    public function __construct()
123
    {
124 5
        $this->acl = new midcom_services_auth_acl($this);
125
126
        // Initialize from midgard
127 5
        if (   midcom_connection::get_user()
128 5
            && $user = $this->get_user(midcom_connection::get_user())) {
129
            $this->set_user($user);
130
        }
131
132 5
        $this->_prepare_authentication_drivers();
133 5
    }
134
135
    /**
136
     * Checks if the current authentication fronted has new credentials
137
     * ready. If yes, it processes the login accordingly. Otherwise look for existing session
138
     *
139
     * @param Request $request The request object
140
     */
141
    public function check_for_login_session(Request $request)
142
    {
143
        // Try to start up a new session, this will authenticate as well.
144
        if ($credentials = $this->frontend->read_login_data($request)) {
145
            if (!$this->login($credentials['username'], $credentials['password'], $request->getClientIp())) {
146
                return;
147
            }
148
            debug_add('Authentication was successful, we have a new login session now. Updating timestamps');
149
150
            $person_class = midcom::get()->config->get('person_class');
151
            $person = new $person_class($this->user->guid);
152
            if (   midcom::get()->config->get('auth_save_prev_login')
153
                && $person->get_parameter('midcom', 'last_login')) {
154
                $person->set_parameter('midcom', 'prev_login', $person->get_parameter('midcom', 'last_login'));
155
            }
156
157
            $person->set_parameter('midcom', 'last_login', time());
158
159
            if (!$person->get_parameter('midcom', 'first_login')) {
160
                $person->set_parameter('midcom', 'first_login', time());
161
            }
162
163
            // Now we check whether there is a success-relocate URL given somewhere.
164
            if ($request->get('midcom_services_auth_login_success_url')) {
165
                midcom::get()->relocate($request->get('midcom_services_auth_login_success_url'));
166
                // This will exit.
167
            }
168
        }
169
        // No new login detected, so we check if there is a running session.
170
        elseif ($user = $this->backend->check_for_active_login_session($request)) {
171
            $this->set_user($user);
172
        }
173
    }
174
175
    /**
176
     * @param midcom_core_user $user
177
     */
178 58
    private function set_user(midcom_core_user $user)
179
    {
180 58
        $this->user = $user;
181 58
        $this->admin = $user->is_admin();
182 58
    }
183
184
    /**
185
     * Internal startup helper, loads all configured authentication drivers.
186
     */
187 5
    private function _prepare_authentication_drivers()
188
    {
189 5
        $classname = midcom::get()->config->get('auth_backend');
190 5
        if (strpos($classname, "_") === false) {
191 5
            $classname = 'midcom_services_auth_backend_' . $classname;
192
        }
193 5
        $this->backend = new $classname($this);
194
195 5
        $classname = midcom::get()->config->get('auth_frontend');
196 5
        if (strpos($classname, "_") === false) {
197 5
            $classname = 'midcom_services_auth_frontend_' . $classname;
198
        }
199 5
        $this->frontend = new $classname();
200 5
    }
201
202
    /**
203
     * Checks whether a user has a certain privilege on the given content object.
204
     * Works on the currently authenticated user by default, but can take another
205
     * user as an optional argument.
206
     *
207
     * @param string $privilege The privilege to check for
208
     * @param MidgardObject $content_object A Midgard Content Object
0 ignored issues
show
Bug introduced by
The type MidgardObject was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
209
     * @param midcom_core_user $user The user against which to check the privilege, defaults to the currently authenticated user.
210
     *     You may specify "EVERYONE" instead of an object to check what an anonymous user can do.
211
     * @return boolean True if the privilege has been granted, false otherwise.
212
     */
213 468
    public function can_do($privilege, $content_object, $user = null)
214
    {
215 468
        if (!is_object($content_object)) {
216 1
            return false;
217
        }
218
219 468
        if ($this->is_admin($user)) {
220
            // Administrators always have access.
221 1
            return true;
222
        }
223
224 468
        $user_id = $this->acl->get_user_id($user);
225
226
        //if we're handed the correct object type, we use its class right away
227 468
        if (midcom::get()->dbclassloader->is_midcom_db_object($content_object)) {
228 468
            $content_object_class = get_class($content_object);
229
        }
230
        //otherwise, we assume (hope) that it's a midgard object
231
        else {
232
            $content_object_class = midcom::get()->dbclassloader->get_midcom_class_name_for_mgdschema_object($content_object);
233
        }
234
235 468
        return $this->acl->can_do_byguid($privilege, $content_object->guid, $content_object_class, $user_id);
236
    }
237
238 468
    private function is_admin($user)
239
    {
240 468
        if ($user === null) {
241 468
            return $this->user && $this->admin;
242
        }
243 3
        if (is_a($user, midcom_core_user::class)) {
244 1
            return $user->is_admin();
245
        }
246 2
        return false;
247
    }
248
249
    /**
250
     * Checks, whether the given user have the privilege assigned to him in general.
251
     * Be aware, that this does not take any permissions overridden by content objects
252
     * into account. Whenever possible, you should user the can_do() variant of this
253
     * call therefore. can_user_do is only of interest in cases where you do not have
254
     * any content object available, for example when creating root topics.
255
     *
256
     * @param string $privilege The privilege to check for
257
     * @param midcom_core_user $user The user against which to check the privilege, defaults to the currently authenticated user,
258
     *     you may specify 'EVERYONE' here to check what an anonymous user can do.
259
     * @param string $class Optional parameter to set if the check should take type specific permissions into account. The class must be default constructible.
260
     * @return boolean True if the privilege has been granted, false otherwise.
261
     */
262 435
    public function can_user_do($privilege, $user = null, $class = null)
263
    {
264 435
        if ($this->is_admin($user)) {
265
            // Administrators always have access.
266 1
            return true;
267
        }
268 435
        if ($this->_component_sudo) {
269 412
            return true;
270
        }
271 29
        if ($user === null) {
272 29
            $user =& $this->user;
273
        }
274
275 29
        if ($user == 'EVERYONE') {
0 ignored issues
show
introduced by
The condition $user == 'EVERYONE' is always false.
Loading history...
276
            $user = null;
277
        }
278
279 29
        return $this->acl->can_do_byclass($privilege, $user, $class);
280
    }
281
282
    /**
283
     * Request superuser privileges for the domain passed.
284
     *
285
     * STUB IMPLEMENTATION ONLY, WILL ALWAYS GRANT SUDO.
286
     *
287
     * You have to call midcom_services_auth::drop_sudo() as soon as you no longer
288
     * need the elevated privileges, which will reset the authentication data to the
289
     * initial credentials.
290
     *
291
     * @param string $domain The domain to request sudo for. This is a component name.
292
     * @return boolean True if admin privileges were granted, false otherwise.
293
     */
294 730
    public function request_sudo($domain = null)
295
    {
296 730
        if (!midcom::get()->config->get('auth_allow_sudo')) {
297 1
            debug_add("SUDO is not allowed on this website.", MIDCOM_LOG_ERROR);
298 1
            return false;
299
        }
300
301 730
        if ($domain === null) {
302 1
            $domain = midcom_core_context::get()->get_key(MIDCOM_CONTEXT_COMPONENT);
303 1
            debug_add("Domain was not supplied, falling back to '{$domain}' which we got from the current component context.");
304
        }
305
306 730
        if ($domain == '') {
307 1
            debug_add("SUDO request for an empty domain, this should not happen. Denying sudo.", MIDCOM_LOG_INFO);
308 1
            return false;
309
        }
310
311 730
        $this->_component_sudo++;
312
313 730
        debug_add("Entered SUDO mode for domain {$domain}.", MIDCOM_LOG_INFO);
314
315 730
        return true;
316
    }
317
318
    /**
319
     * Drops previously acquired superuser privileges.
320
     *
321
     * @see request_sudo()
322
     */
323 730
    public function drop_sudo()
324
    {
325 730
        if ($this->_component_sudo > 0) {
326 730
            debug_add('Leaving SUDO mode.');
327 730
            $this->_component_sudo--;
328
        } else {
329
            debug_add('Requested to leave SUDO mode, but sudo was already disabled. Ignoring request.', MIDCOM_LOG_INFO);
330
        }
331 730
    }
332
333 730
    public function is_component_sudo()
334
    {
335 730
        return $this->_component_sudo > 0;
336
    }
337
338
    /**
339
     * Check, whether a user is member of a given group. By default, the query is run
340
     * against the currently authenticated user.
341
     *
342
     * It always returns true for administrative users.
343
     *
344
     * @param mixed $group Group to check against, this can be either a midcom_core_group object or a group string identifier.
345
     * @param midcom_core_user $user The user which should be checked, defaults to the current user.
346
     * @return boolean Indicating membership state.
347
     */
348
    public function is_group_member($group, $user = null)
349
    {
350
        if ($this->is_admin($user)) {
351
            // Administrators always have access.
352
            return true;
353
        }
354
        // Default parameter
355
        if ($user === null) {
356
            if ($this->user === null) {
357
                // not authenticated
358
                return false;
359
            }
360
            $user = $this->user;
361
        }
362
363
        return $user->is_in_group($group);
364
    }
365
366
    /**
367
     * Returns true if there is an authenticated user, false otherwise.
368
     *
369
     * @return boolean True if there is a user logged in.
370
     */
371 148
    public function is_valid_user()
372
    {
373 148
        return $this->user !== null;
374
    }
375
376
    /**
377
     * Validates that the current user has the given privilege granted on the
378
     * content object passed to the function.
379
     *
380
     * If this is not the case, an Access Denied error is generated, the message
381
     * defaulting to the string 'access denied: privilege %s not granted' of the
382
     * MidCOM main L10n table.
383
     *
384
     * The check is always done against the currently authenticated user. If the
385
     * check is successful, the function returns silently.
386
     *
387
     * @param string $privilege The privilege to check for
388
     * @param MidgardObject $content_object A Midgard Content Object
389
     * @param string $message The message to show if the privilege has been denied.
390
     */
391 182
    public function require_do($privilege, $content_object, $message = null)
392
    {
393 182
        if (!$this->can_do($privilege, $content_object)) {
394
            throw $this->access_denied($message, 'privilege %s not granted', $privilege);
395
        }
396 182
    }
397
398
    /**
399
     * Validates, whether the given user have the privilege assigned to him in general.
400
     * Be aware, that this does not take any permissions overridden by content objects
401
     * into account. Whenever possible, you should user the require_do() variant of this
402
     * call therefore. require_user_do is only of interest in cases where you do not have
403
     * any content object available, for example when creating root topics.
404
     *
405
     * If this is not the case, an Access Denied error is generated, the message
406
     * defaulting to the string 'access denied: privilege %s not granted' of the
407
     * MidCOM main L10n table.
408
     *
409
     * The check is always done against the currently authenticated user. If the
410
     * check is successful, the function returns silently.
411
     *
412
     * @param string $privilege The privilege to check for
413
     * @param string $message The message to show if the privilege has been denied.
414
     * @param string $class Optional parameter to set if the check should take type specific permissions into account. The class must be default constructible.
415
     */
416 76
    public function require_user_do($privilege, $message = null, $class = null)
417
    {
418 76
        if (!$this->can_user_do($privilege, null, $class)) {
419
            throw $this->access_denied($message, 'privilege %s not granted', $privilege);
420
        }
421 76
    }
422
423
    /**
424
     * Validates that the current user is a member of the given group.
425
     *
426
     * If this is not the case, an Access Denied error is generated, the message
427
     * defaulting to the string 'access denied: user is not member of the group %s' of the
428
     * MidCOM main L10n table.
429
     *
430
     * The check is always done against the currently authenticated user. If the
431
     * check is successful, the function returns silently.
432
     *
433
     * @param mixed $group Group to check against, this can be either a midcom_core_group object or a group string identifier.
434
     * @param string $message The message to show if the user is not member of the given group.
435
     */
436
    function require_group_member($group, $message = null)
437
    {
438
        if (!$this->is_group_member($group)) {
439
            if (is_object($group)) {
440
                $group = $group->name;
441
            }
442
            throw $this->access_denied($message, 'user is not member of the group %s', $group);
443
        }
444
    }
445
446
    /**
447
     * Validates that we currently have admin level privileges, which can either
448
     * come from the current user, or from the sudo service.
449
     *
450
     * If the check is successful, the function returns silently.
451
     *
452
     * @param string $message The message to show if the admin level privileges are missing.
453
     */
454 3
    public function require_admin_user($message = null)
455
    {
456 3
        if (!$this->admin && !$this->_component_sudo) {
457
            throw $this->access_denied($message, 'admin level privileges required');
458
        }
459 3
    }
460
461
    private function access_denied($message, $fallback, $data = null)
462
    {
463
        if ($message === null) {
464
            $message = midcom::get()->i18n->get_string('access denied: ' . $fallback, 'midcom');
465
            if ($data !== null) {
466
                $message = sprintf($message, $data);
467
            }
468
        }
469
        debug_print_function_stack("access_denied was called from here:");
470
        return new midcom_error_forbidden($message);
471
    }
472
473
    /**
474
     * Require either a configured IP address or admin credentials
475
     *
476
     * @param string $domain Domain for IP sudo
477
     * @throws midcom_error In case request_sudo fails
478
     * @return boolean True if IP sudo is active, false otherwise
479
     */
480
    public function require_admin_or_ip($domain)
481
    {
482
        $ips = midcom::get()->config->get('indexer_reindex_allowed_ips');
483
        if (   $ips
484
            && in_array($_SERVER['REMOTE_ADDR'], $ips)) {
485
            if (!$this->request_sudo($domain)) {
486
                throw new midcom_error('Failed to acquire SUDO rights. Aborting.');
487
            }
488
            return true;
489
        }
490
491
        // Require user to Basic-authenticate for security reasons
492
        $this->require_valid_user('basic');
493
        $this->require_admin_user();
494
        return false;
495
    }
496
497
    /**
498
     * Validates that there is an authenticated user.
499
     *
500
     * If this is not the case, midcom_error_forbidden is thrown, or a
501
     * basic auth challenge is triggered
502
     *
503
     * If the check is successful, the function returns silently.
504
     *
505
     * @param string $method Preferred authentication method: form or basic
506
     */
507 146
    public function require_valid_user($method = 'form')
508
    {
509 146
        if (!$this->is_valid_user()) {
510
            if ($method !== 'basic') {
511
                throw new midcom_error_forbidden(null, MIDCOM_ERRAUTH);
512
            }
513
            $this->_http_basic_auth();
514
            // This will exit.
515
        }
516 146
    }
517
518
    /**
519
     * Handles HTTP Basic authentication
520
     */
521
    private function _http_basic_auth()
522
    {
523
        if (!isset($_SERVER['PHP_AUTH_USER'])) {
524
            midcom::get()->header("WWW-Authenticate: Basic realm=\"Midgard\"");
525
            midcom::get()->header('HTTP/1.0 401 Unauthorized');
526
            // TODO: more fancy 401 output ?
527
            echo "<h1>Authorization required</h1>\n";
528
            midcom::get()->finish();
529
        } elseif ($user = $this->backend->authenticate($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])) {
530
            $this->set_user($user);
531
        } else {
532
            // Wrong password: Recurse until auth ok or user gives up
533
            unset($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
534
            $this->_http_basic_auth();
535
        }
536
    }
537
538
    /**
539
     * Resolve any assignee identifier known by the system into an appropriate user/group object.
540
     *
541
     * @param string $id A valid user or group identifier usable as assignee (e.g. the $id member
542
     *     of any midcom_core_user or midcom_core_group object).
543
     * @return object|false corresponding object or false on failure.
544
     */
545 121
    public function get_assignee($id)
546
    {
547 121
        $parts = explode(':', $id);
548
549 121
        if ($parts[0] == 'user') {
550 120
            return $this->get_user($id);
551
        }
552 4
        if ($parts[0] == 'group') {
553 3
            return $this->get_group($id);
554
        }
555 1
        debug_add("The identifier {$id} cannot be resolved into an assignee, it cannot be mapped to a type.", MIDCOM_LOG_WARN);
556
557 1
        return null;
558
    }
559
560
    /**
561
     * This is a wrapper for get_user, which allows user retrieval by its name.
562
     * If the username is unknown, false is returned.
563
     *
564
     * @param string $name The name of the user to look up.
565
     * @return midcom_core_user|false The user object matching the username, or false if the username is unknown.
566
     */
567 3
    public function get_user_by_name($name)
568
    {
569 3
        $mc = new midgard_collector('midgard_user', 'login', $name);
570 3
        $mc->set_key_property('person');
571 3
        $mc->add_constraint('authtype', '=', midcom::get()->config->get('auth_type'));
572 3
        $mc->execute();
573 3
        $keys = $mc->list_keys();
574 3
        if (count($keys) != 1) {
575
            return false;
576
        }
577
578 3
        $person_class = midcom::get()->config->get('person_class');
579 3
        $person = new $person_class(key($keys));
580
581 3
        return $this->get_user($person);
582
    }
583
584
    /**
585
     * This is a wrapper for get_group, which allows Midgard Group retrieval by its name.
586
     * If the group name is unknown, false is returned.
587
     *
588
     * In the case that more than one group matches the given name, the first one is returned.
589
     * Note, that this should not happen as midgard group names should be unique according to the specs.
590
     *
591
     * @param string $name The name of the group to look up.
592
     * @return midcom_core_group|false The group object matching the name, or false if the group name is unknown.
593
     */
594
    public function & get_midgard_group_by_name($name)
595
    {
596
        $qb = new midgard_query_builder('midgard_group');
597
        $qb->add_constraint('name', '=', $name);
598
599
        $result = $qb->execute();
600
        if (empty($result)) {
601
            $result = false;
602
            return $result;
603
        }
604
        $grp = $this->get_group($result[0]);
605
        return $grp;
606
    }
607
608
    /**
609
     * Load a user from the database and returns an object instance.
610
     *
611
     * @param mixed $id A valid identifier for a MidgardPerson: An existing midgard_person class
612
     *     or subclass thereof, a Person ID or GUID or a midcom_core_user identifier.
613
     * @return midcom_core_user|false The user object matching the identifier or false on failure.
614
     */
615 160
    public function get_user($id)
616
    {
617 160
        $param = $id;
618
619 160
        if (isset($param->id)) {
620 3
            $id = $param->id;
621 160
        } elseif (!is_string($id) && !is_int($id)) {
622
            debug_print_type('The passed argument was an object of an unsupported type:', $param, MIDCOM_LOG_WARN);
623
            debug_print_r('Complete object dump:', $param);
624
            return false;
625
        }
626 160
        if (!array_key_exists($id, $this->_user_cache)) {
627
            try {
628 111
                if (is_a($param, midcom_db_person::class)) {
629
                    $param = $param->__object;
630
                }
631 111
                $this->_user_cache[$id] = new midcom_core_user($param);
632 6
            } catch (midcom_error $e) {
633
                // Keep it silent while missing user object can mess here
634 6
                $this->_user_cache[$id] = false;
635
            }
636
        }
637
638 160
        return $this->_user_cache[$id];
639
    }
640
641
    /**
642
     * Returns a midcom_core_group instance. Valid arguments are either a valid group identifier
643
     * (group:...), any valid identifier for the midcom_core_group
644
     * constructor or a valid object of that type.
645
     *
646
     * @param mixed $id The identifier of the group as outlined above.
647
     * @return midcom_core_group|false A group object instance matching the identifier, or false on failure.
648
     */
649 3
    public function get_group($id)
650
    {
651 3
        $param = $id;
652
653 3
        if (isset($param->id)) {
654
            $id = $param->id;
655 3
        } elseif (!is_string($id) && !is_int($id)) {
656
            debug_print_type('The group identifier is of an unsupported type:', $param, MIDCOM_LOG_WARN);
657
            debug_print_r('Complete dump:', $param);
658
            return false;
659
        }
660
661 3
        if (!array_key_exists($id, $this->_group_cache)) {
662
            try {
663 3
                if (is_a($param, midcom_core_dbaobject::class)) {
664
                    $param = $param->__object;
665
                }
666 3
                $this->_group_cache[$id] = new midcom_core_group($param);
667
            } catch (midcom_error $e) {
668
                debug_add("Group with identifier {$id} could not be loaded: " . $e->getMessage(), MIDCOM_LOG_WARN);
669
                $this->_group_cache[$id] = false;
670
            }
671
        }
672 3
        return $this->_group_cache[$id];
673
    }
674
675
    /**
676
     * This call tells the backend to log in.
677
     */
678 58
    public function login($username, $password, $clientip = null)
679
    {
680 58
        if ($user = $this->backend->login($username, $password, $clientip)) {
681 58
            $this->set_user($user);
682 58
            return true;
683
        }
684
        debug_add('The login information for ' . $username . ' was invalid.', MIDCOM_LOG_WARN);
685
        return false;
686
    }
687
688
    public function trusted_login($username)
689
    {
690
        if (midcom::get()->config->get('auth_allow_trusted') !== true) {
691
            debug_add("Trusted logins are prohibited", MIDCOM_LOG_ERROR);
692
            return false;
693
        }
694
695
        if ($user = $this->backend->login($username, '', null, true)) {
696
            $this->set_user($user);
697
            return true;
698
        }
699
        return false;
700
    }
701
702
    /**
703
     * This call clears any authentication state
704
     */
705 1
    public function logout()
706
    {
707 1
        if ($this->user === null) {
708
            debug_add('The backend has no authenticated user set, so we should be fine');
709
        } else {
710 1
            $this->backend->logout($this->user);
711 1
            $this->user = null;
712
        }
713 1
        $this->admin = false;
714 1
    }
715
716
    /**
717
     * Render the main login form.
718
     * This only includes the form, no heading or whatsoever.
719
     *
720
     * It is recommended to call this function only as long as the headers are not yet sent (which
721
     * is usually given thanks to MidCOMs output buffering).
722
     *
723
     * What gets rendered depends on the authentication frontend, but will usually be some kind
724
     * of form.
725
     */
726
    public function show_login_form()
727
    {
728
        $this->frontend->show_login_form();
729
    }
730
731
    /**
732
     * Show a complete login page unconditionally and exit afterwards.
733
     * If the current style has an element called <i>midcom_services_auth_login_page</i>
734
     * it will be shown instead. The local scope will contain the two variables
735
     * $title and $login_warning. $title is the localized string 'login' from the main
736
     * MidCOM L10n DB, login_warning is empty unless there was a failed authentication
737
     * attempt, in which case it will have a localized warning message enclosed in a
738
     * paragraph with the ID 'login_warning'.
739
     */
740
    public function show_login_page()
741
    {
742
        $this->frontend->show_login_page();
743
    }
744
745
    public function show_access_denied($message)
746
    {
747
        $this->frontend->show_access_denied($message);
748
    }
749
}
750