Passed
Push — master ( bee4ae...fc5262 )
by Andreas
16:28
created

midcom_services_auth_backend::login()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3.0987

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 4
dl 0
loc 13
ccs 7
cts 9
cp 0.7778
crap 3.0987
rs 10
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
 * Authentication backend, responsible for validating user/password pairs and
13
 * mapping them to a given user as well as the "sessioning" part, e.g. the transition
14
 * of the authentication credentials over several requests.
15
 *
16
 * Configuration, if necessary, should be done using the MidCOM configuration
17
 * system, prefixing all values with 'auth_backend_$name_', e.g.
18
 * 'auth_backend_cookie_timeout'.
19
 *
20
 * @package midcom.services
21
 */
22
abstract class midcom_services_auth_backend
23
{
24
    /**
25
     * @var midcom_services_auth
26
     */
27
    protected $auth;
28
29
    /**
30
     * The constructor should do only basic initialization.
31
     *
32
     * @param midcom_services_auth $auth Main authentication instance
33
     */
34
    public function __construct(midcom_services_auth $auth)
35
    {
36
        $this->auth = $auth;
37
    }
38
39
    /**
40
     * This function, always called first in the order of execution, should check
41
     * whether we have a usable login session. It has to use the login session management
42
     * system to load a login session. At the end of the successful execution of this
43
     * function, you have to populate the $session and $user members accordingly.
44
     *
45
     * @param Request $request The request object
46
     * @return boolean|array Return clientip, userid and timeout if the login session was successfully loaded, false
47
     *     otherwise.
48
     */
49
    abstract public function read_session(Request $request);
50
51
    /**
52
     * This is called immediately after a new login
53
     * The authentication driver has to ensure that the login identifier stays
54
     * available during subsequent requests.
55
     *
56
     * @param string $clientip
57
     * @param midcom_core_user $user
58
     * @return boolean Indicating success
59
     */
60
    abstract public function create_session($clientip, midcom_core_user $user);
61
62
    /**
63
     * This should delete the currently active login session,
64
     * which has been loaded by a previous call to read_session or created during
65
     * create_session.
66
     *
67
     * You should throw midcom_error if anything goes wrong here.
68
     */
69
    abstract public function delete_session();
70
71
    /**
72
     * Refresh the session's timestamp here
73
     */
74
    abstract public function update_session();
75
76
    /**
77
     * Checks for a running login session.
78
     *
79
     * @param Request $request The request object
80
     * @return boolean|midcom_core_user
81
     */
82 1
    public function check_for_active_login_session(Request $request)
83
    {
84 1
        if (!$data = $this->read_session($request)) {
85 1
            return false;
86
        }
87
88
        if (   midcom::get()->config->get('auth_check_client_ip')
89
            && $data['clientip'] != $request->getClientIp()) {
90
            debug_add("The session had mismatching client IP.", MIDCOM_LOG_INFO);
91
            debug_add("Expected {$data['clientip']}, got {$request->getClientIp()}.");
92
            return false;
93
        }
94
95
        if (!$user = $this->auth->get_user($data['userid'])) {
96
            debug_add("The user ID {$data['userid']} is invalid, could not load the user from the database, assuming tampered session.",
97
            MIDCOM_LOG_ERROR);
98
            $this->delete_session();
99
            return false;
100
        }
101
102
        if (   !$this->check_timestamp($data['timestamp'], $user)
103
            || !$this->authenticate($user->username, '', true)) {
104
            $this->logout($user);
105
            return false;
106
        }
107
        return $user;
108
    }
109
110
    private function check_timestamp($timestamp, midcom_core_user $user) : bool
111
    {
112
        $timeout = midcom::get()->config->get('auth_login_session_timeout', 0);
113
        if ($timeout > 0 && time() - $timeout > $timestamp) {
114
            debug_add("The session has timed out.", MIDCOM_LOG_INFO);
115
            return false;
116
        }
117
118
        if ($timestamp < time() - midcom::get()->config->get('auth_login_session_update_interval')) {
119
            // Update the timestamp if previous timestamp is older than specified interval
120
            $this->update_session();
121
            $person = $user->get_storage()->__object;
122
            $person->set_parameter('midcom', 'online', time());
123
        }
124
        return true;
125
    }
126
127
    /**
128
     * Does the actual Midgard authentication.
129
     *
130
     * @param string $username The name of the user to authenticate.
131
     * @param string $password The password of the user to authenticate.
132
     * @param boolean $trusted
133
     * @return boolean|midcom_core_user
134
     */
135 58
    public function authenticate($username, $password, $trusted = false)
136
    {
137 58
        if (empty($username)) {
138
            debug_add("Failed to authenticate: Username is empty.", MIDCOM_LOG_ERROR);
139
            return false;
140
        }
141 58
        if (!$trusted && empty($password)) {
142
            debug_add("Failed to authenticate: Password is empty.", MIDCOM_LOG_ERROR);
143
            return false;
144
        }
145
146 58
        $user = midcom_connection::login($username, $password, $trusted);
147
148 58
        if (!$user) {
149
            debug_add("Failed to authenticate the given user: ". midcom_connection::get_error_string(),
150
                    MIDCOM_LOG_INFO);
151
            return false;
152
        }
153
154 58
        return $this->auth->get_user($user->person);
155
    }
156
157
    /**
158
     * Creates a login session using the given credentials. It assumes that
159
     * no login has concluded earlier
160
     *
161
     * @param string $username The name of the user to authenticate.
162
     * @param string $password The password of the user to authenticate.
163
     * @param string $clientip The client IP to which this session is assigned to. This
164
     *     defaults to the client IP reported by the web server
165
     * @param boolean $trusted Do a trusted login
166
     * @return boolean|midcom_core_user
167
     */
168 58
    public function login($username, $password, $clientip = null, $trusted = false)
169
    {
170 58
        $user = $this->authenticate($username, $password, $trusted);
171 58
        if (!$user) {
172
            return false;
173
        }
174
175 58
        if ($this->create_session($clientip, $user)) {
176 58
            $person = $user->get_storage()->__object;
177 58
            $person->set_parameter('midcom', 'online', time());
178 58
            return $user;
179
        }
180
        return false;
181
    }
182
183
    /**
184
     * Deletes login information and session
185
     */
186 1
    public function logout(midcom_core_user $user)
187
    {
188 1
        if ($person = $user->get_storage()) {
189 1
            $person->__object->delete_parameters(['domain' => 'midcom', 'name' => 'online']);
190
        }
191 1
        $this->delete_session();
192 1
        midcom_connection::logout();
193 1
    }
194
}
195