writeJsConnect()   F
last analyzed

Complexity

Conditions 23
Paths 224

Size

Total Lines 66
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 23
eloc 48
c 1
b 0
f 0
nc 224
nop 5
dl 0
loc 66
rs 3.0333

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * This file contains the client code for Vanilla jsConnect single sign on.
4
 *
5
 * @author Todd Burry <[email protected]>
6
 * @version 2.0
7
 * @copyright 2008-2017 Vanilla Forums, Inc.
8
 * @license GNU GPLv2 http://www.opensource.org/licenses/gpl-2.0.php
9
 */
10
11
define('JS_CONNECT_VERSION', '2');
12
define('JS_TIMEOUT', 24 * 60);
13
14
/**
15
 * Write the jsConnect string for single sign on.
16
 *
17
 * @param array $user An array containing information about the currently signed on user. If no user is signed in then this should be an empty array.
18
 * @param array $request An array of the $_GET request.
19
 * @param string $clientID The string client ID that you set up in the jsConnect settings page.
20
 * @param string $secret The string secret that you set up in the jsConnect settings page.
21
 * @param string|bool $secure Whether or not to check for security. This is one of these values.
22
 *  - true: Check for security and sign the response with an md5 hash.
23
 *  - false: Don't check for security, but sign the response with an md5 hash.
24
 *  - string: Check for security and sign the response with the given hash algorithm. See hash_algos() for what your server can support.
25
 *  - null: Don't check for security and don't sign the response.
26
 * @since 1.1b Added the ability to provide a hash algorithm to $secure.
27
 */
28
function writeJsConnect($user, $request, $clientID, $secret, $secure = true) {
29
    $user = array_change_key_case($user);
30
31
    // Error checking.
32
    if ($secure) {
33
        // Check the client.
34
        if (!isset($request['v'])) {
35
            $error = array('error' => 'invalid_request', 'message' => 'Missing the v parameter.');
36
        } elseif ($request['v'] !== JS_CONNECT_VERSION) {
37
            $error = array('error' => 'invalid_request', 'message' => "Unsupported version {$request['v']}.");
38
        } elseif (!isset($request['client_id'])) {
39
            $error = array('error' => 'invalid_request', 'message' => 'Missing the client_id parameter.');
40
        } elseif ($request['client_id'] != $clientID) {
41
            $error = array('error' => 'invalid_client', 'message' => "Unknown client {$request['client_id']}.");
42
        } elseif (!isset($request['timestamp']) && !isset($request['sig'])) {
43
            if (is_array($user) && count($user) > 0) {
44
                // This isn't really an error, but we are just going to return public information when no signature is sent.
45
                $error = array('name' => (string)@$user['name'], 'photourl' => @$user['photourl'], 'signedin' => true);
46
            } else {
47
                $error = array('name' => '', 'photourl' => '');
48
            }
49
        } elseif (!isset($request['timestamp']) || !ctype_digit($request['timestamp'])) {
50
            $error = array('error' => 'invalid_request', 'message' => 'The timestamp parameter is missing or invalid.');
51
        } elseif (!isset($request['sig'])) {
52
            $error = array('error' => 'invalid_request', 'message' => 'Missing the sig parameter.');
53
        } // Make sure the timestamp hasn't timedout
54
        elseif (abs($request['timestamp'] - JsTimestamp()) > JS_TIMEOUT) {
55
            $error = array('error' => 'invalid_request', 'message' => 'The timestamp is invalid.');
56
        } elseif (!isset($request['nonce'])) {
57
            $error = array('error' => 'invalid_request', 'message' => 'Missing the nonce parameter.');
58
        } elseif (!isset($request['ip'])) {
59
            $error = array('error' => 'invalid_request', 'message' => 'Missing the ip parameter.');
60
        } else {
61
            $signature = jsHash($request['ip'].$request['nonce'].$request['timestamp'].$secret, $secure);
62
            if ($signature != $request['sig']) {
63
                $error = array('error' => 'access_denied', 'message' => 'Signature invalid.');
64
            }
65
        }
66
    }
67
68
    if (isset($error)) {
69
        $result = $error;
70
    } elseif (is_array($user) && count($user) > 0) {
71
        if ($secure === null) {
0 ignored issues
show
introduced by
The condition $secure === null is always false.
Loading history...
72
            $result = $user;
73
        } else {
74
            $user['ip'] = $request['ip'];
75
            $user['nonce'] = $request['nonce'];
76
            $result = signJsConnect($user, $clientID, $secret, $secure, true);
77
            $result['v'] = JS_CONNECT_VERSION;
78
        }
79
    } else {
80
        $result = array('name' => '', 'photourl' => '');
81
    }
82
83
    $content = json_encode($result);
84
85
    if (isset($request['callback'])) {
86
        $content = "{$request['callback']}($content)";
87
    }
88
89
    if (!headers_sent()) {
90
        $contentType = jsConnectContentType($request);
91
        header($contentType, true);
92
    }
93
    echo $content;
94
}
95
96
/**
97
 *
98
 *
99
 * @param $data
100
 * @param $clientID
101
 * @param $secret
102
 * @param $hashType
103
 * @param bool $returnData
104
 * @return array|string
105
 */
106
function signJsConnect($data, $clientID, $secret, $hashType, $returnData = false) {
107
    $normalizedData = array_change_key_case($data);
108
    ksort($normalizedData);
109
110
    foreach ($normalizedData as $key => $value) {
111
        if ($value === null) {
112
            $normalizedData[$key] = '';
113
        }
114
    }
115
116
    // RFC1738 state that spaces are encoded as '+'.
117
    $stringifiedData = http_build_query($normalizedData, null, '&', PHP_QUERY_RFC1738);
118
    $signature = jsHash($stringifiedData.$secret, $hashType);
119
    if ($returnData) {
120
        $normalizedData['client_id'] = $clientID;
121
        $normalizedData['sig'] = $signature;
122
        return $normalizedData;
123
    } else {
124
        return $signature;
125
    }
126
}
127
128
/**
129
 * Return the hash of a string.
130
 *
131
 * @param string $string The string to hash.
132
 * @param string|bool $secure The hash algorithm to use. true means md5.
133
 * @return string
134
 */
135
function jsHash($string, $secure = true) {
136
    if ($secure === true) {
137
        $secure = 'md5';
138
    }
139
140
    switch ($secure) {
141
        case 'sha1':
142
            return sha1($string);
143
            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
144
        case 'md5':
145
        case false:
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $secure of type false|string against false; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
146
            return md5($string);
147
        default:
148
            return hash($secure, $string);
149
    }
150
}
151
152
/**
153
 *
154
 *
155
 * @return int
156
 */
157
function jsTimestamp() {
158
    return time();
159
}
160
161
/**
162
 * Generate an SSO string suitable for passing in the url for embedded SSO.
163
 *
164
 * @param array $user The user to sso.
165
 * @param string $clientID Your client ID.
166
 * @param string $secret Your secret.
167
 * @return string
168
 */
169
function jsSSOString($user, $clientID, $secret) {
170
    if (!isset($user['client_id'])) {
171
        $user['client_id'] = $clientID;
172
    }
173
174
    $string = base64_encode(json_encode($user));
175
    $timestamp = time();
176
    $hash = hash_hmac('sha1', "$string $timestamp", $secret);
177
178
    $result = "$string $hash $timestamp hmacsha1";
179
    return $result;
180
}
181
182
/**
183
 * Based on a jsConnect request, determine the proper response content type.
184
 *
185
 * @param array $request
186
 * @return string
187
 */
188
function jsConnectContentType(array $request): string {
189
    $isJsonp = isset($request["callback"]);
190
    $contentType = $isJsonp ? "Content-Type: application/javascript; charset=utf-8" : "Content-Type: application/json; charset=utf-8";
191
    return $contentType;
192
}
193