Passed
Push — main ( d07ee1...38048d )
by Rafael
50:23
created

DolibarrAuth::checkToken()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 2
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace DoliLib;
4
5
use DoliCore\Base\Config;
6
use DoliCore\Tools\Load;
7
8
class DolibarrAuth
9
{
10
    private const COOKIE_NAME = 'dol_login';
11
    private const COOKIE_USER = self::COOKIE_NAME . '_user';
12
    private const COOKIE_EXPIRE_TIME = 30 * 86400; // 30 days
13
    private const COOKIE_SAMESITE = 'Strict';
14
15
    public static $user = null;
16
17
    public static function isLogged()
18
    {
19
        $userId = FILTER_INPUT(INPUT_COOKIE, self::COOKIE_USER);
20
        $token = FILTER_INPUT(INPUT_COOKIE, self::COOKIE_NAME);
21
        if (empty($token)) {
22
            return false;
23
        }
24
25
        self::$user = Load::getUser();
26
        $result = self::$user->fetch($userId, '', '', 1, 1);
27
        if ($result <= 0) {
28
            return false;
29
        }
30
31
        return self::checkToken(self::$user->login, $token);
32
    }
33
34
    private static function checkToken($username, $token)
35
    {
36
        $token_file = self::getTokenFilename($username);
37
        if (!file_exists($token_file)) {
38
            return false;
39
        }
40
        $stored_token = file_get_contents($token_file);
41
        return $token === $stored_token;
42
    }
43
44
    private static function getTokenFilename($username)
45
    {
46
        $tokens_path = realpath(BASE_PATH . '/..') . '/tmp/tokens/';
47
        if (!is_dir($tokens_path) && !mkdir($tokens_path, 0777, true) && !is_dir($tokens_path)) {
48
            die('Could not create tokens directory:' . $tokens_path);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
49
        }
50
51
        return $tokens_path . md5($username) . '.token';
52
    }
53
54
    public static function login($username, $password, $entity = 1)
55
    {
56
        $conf = Config::getConfig();
57
        $mode = $conf->security->authentication_method ?? 'dolibarr';
58
        $authmode = explode(',', $mode);
59
        if (!self::checkLogin($authmode, $username, $password, $entity)) {
60
            return false;
61
        }
62
63
        return static::setSession($username);
64
    }
65
66
    private static function checkLogin($authmode, $user, $pass, $entity): bool
67
    {
68
        foreach ($authmode as $mode) {
69
            $method = realpath(BASE_PATH . '/../Dolibarr/Core/Login') . '/functions_' . $mode . '.php';
70
            if (!file_exists($method)) {
71
                continue;
72
            }
73
            $function = 'check_user_password_' . $mode;
74
            require_once $method;
75
            if ($function($user, $pass, $entity)) {
76
                return true;
77
            }
78
        }
79
80
        return false;
81
    }
82
83
    public static function setSession($username, $entitytotest = 1)
84
    {
85
        $user = Load::getUser();
86
        $result = $user->fetch('', $username, '', 1, ($entitytotest > 0 ? $entitytotest : -1));
87
        if ($result <= 0) {
88
            return false;
89
        }
90
91
        $cookie_options = [
92
            'expires' => time() + self::COOKIE_EXPIRE_TIME,
93
            'path' => '/',
94
            'domain' => $_SERVER['HTTP_HOST'],
95
            'secure' => true, // Ensure the cookie is sent over HTTPS only
96
            'httponly' => true, // Prevent JavaScript from accessing the cookie
97
            'samesite' => self::COOKIE_SAMESITE, // Mitigate CSRF attacks
98
        ];
99
100
        $token = self::generateToken();
101
102
        // Set the user ID cookie securely
103
        setcookie(self::COOKIE_USER, $user->id, $cookie_options);
104
105
        // Set the authentication token cookie securely
106
        setcookie(self::COOKIE_NAME, $token, $cookie_options);
107
108
        if (!self::setToken($username, $token)) {
109
            die('Can`t write to token for ' . $username);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
110
            return false;
0 ignored issues
show
Unused Code introduced by
return false is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
111
        }
112
113
        return true;
114
    }
115
116
    private static function generateToken($length = 32)
117
    {
118
        return bin2hex(random_bytes($length));
119
    }
120
121
    private static function setToken($username, $token)
122
    {
123
        $token_file = self::getTokenFilename($username);
124
        return file_put_contents($token_file, $token) > 0;
125
    }
126
127
}
128