Sso   A
last analyzed

Complexity

Total Complexity 12

Size/Duplication

Total Lines 147
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 12
eloc 54
c 4
b 0
f 0
dl 0
loc 147
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A getPluginSettings() 0 10 2
A output() 0 21 2
A embeddedOutput() 0 14 2
B getSsoData() 0 38 6
1
<?php
2
/**
3
 * Vanillaforums plugin for Craft CMS 3.x
4
 *
5
 * Single Sign On plugin for VanillaForums/jsConnect and CraftCMS
6
 *
7
 * @link      https://nystudio107.com/
8
 * @copyright Copyright (c) 2019 nystudio107
9
 */
10
11
namespace nystudio107\vanillaforums\services;
12
13
use nystudio107\vanillaforums\models\Settings;
14
use nystudio107\vanillaforums\Vanillaforums;
15
use nystudio107\vanillaforums\events\SsoDataEvent;
16
use nystudio107\vanillaforums\models\SsoData;
17
18
use Craft;
19
use craft\base\Component;
20
use craft\web\Response;
21
22
require_once(__DIR__.'/../lib/jsConnectPHP/functions.jsconnect.php');
23
24
/** @noinspection MissingPropertyAnnotationsInspection */
25
26
/**
27
 * @author    nystudio107
28
 * @package   Vanillaforums
29
 * @since     3.0.0
30
 */
31
class Sso extends Component
32
{
33
    // Constants
34
    // =========================================================================
35
36
    /**
37
     * @event SsoDataEvent The event that is triggered before the SSO data is used,
38
     * you may modify the [[SsoDataEvent::data]] as you see fit. You may set
39
     * [[SsoDataEvent::isValid]] to `false` to prevent SSO data from being used.
40
     *
41
     * ```php
42
     * use nystudio107\vanillaforums\services\Sso;
43
     * use nystudio107\vanillaforums\events\SsoDataEvent;
44
     *
45
     * Event::on(Sso::class,
46
     *     SsoDataEvent::EVENT_SSO_DATA,
47
     *     function(SsoDataEvent $event) {
48
     *         // potentially set $event->isValid or modify $event->data
49
     *     }
50
     * );
51
     * ```
52
     */
53
    const EVENT_SSO_DATA = 'vanillaForumsSsoData';
54
55
    // Public Methods
56
    // =========================================================================
57
58
    /**
59
     * Generate the jsConnect string for single sign on
60
     *
61
     * @param int $userId
62
     *
63
     * @return string
64
     * @throws \yii\base\ExitException
65
     */
66
    public function output(int $userId = 0): string
67
    {
68
        $result = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
69
        $settings = $this->getPluginSettings();
70
        $ssoData = $this->getSsoData($userId);
71
        Craft::$app->getResponse()->format = Response::FORMAT_RAW;
72
        if ($ssoData !== null) {
73
            $request = Craft::$app->getRequest();
74
            //ob_start(); // Start output buffering
75
            \WriteJsConnect(
76
                $ssoData->toArray(),
77
                $request->get(),
78
                $settings->vanillaForumsClientID,
79
                $settings->vanillaForumsSecret,
80
                $settings->hashAlgorithm ?? 'md5'
81
            );
82
            //$result = ob_get_contents();
83
            //ob_end_clean(); // Store buffer in variable
84
        }
85
86
        Craft::$app->end();
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
87
        //return $result === false ? '' : $result;
88
    }
89
90
    /**
91
     * Generate an SSO string suitable for passing in the url for embedded SSO
92
     *
93
     * @param int $userId
94
     *
95
     * @return string
96
     */
97
    public function embeddedOutput(int $userId = 0): string
98
    {
99
        $result = '';
100
        $settings = $this->getPluginSettings();
101
        $ssoData = $this->getSsoData($userId);
102
        if ($ssoData !== null) {
103
            $result = \JsSSOString(
104
                $ssoData->toArray(),
105
                $settings->vanillaForumsClientID,
106
                $settings->vanillaForumsSecret
107
            );
108
        }
109
110
        return $result;
111
    }
112
113
    // Private Methods
114
    // =========================================================================
115
116
    /**
117
     * Return an SSOData object filled in with the current user's info, or null
118
     *
119
     * @param int $userId
120
     *
121
     * @return SsoData|null
122
     */
123
    private function getSsoData(int $userId = 0)
124
    {
125
        $data = null;
126
127
        // Assume the currently logged in user if no $userId is passed in
128
        if ($userId === 0) {
129
            $user = Craft::$app->getUser()->getIdentity();
130
        } else {
131
            $users = Craft::$app->getUsers();
132
            $user = $users->getUserById($userId);
133
        }
134
        if ($user) {
135
            $generalConfig = Craft::$app->getConfig()->getGeneral();
136
            $name = $generalConfig->useEmailAsUsername ? $user->getFullName() : $user->username;
0 ignored issues
show
Bug introduced by
The method getFullName() does not exist on yii\web\IdentityInterface. It seems like you code against a sub-type of yii\web\IdentityInterface such as craft\elements\User. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

136
            $name = $generalConfig->useEmailAsUsername ? $user->/** @scrutinizer ignore-call */ getFullName() : $user->username;
Loading history...
137
            $photoUrl = '';
138
            $photo = $user->getPhoto();
0 ignored issues
show
Bug introduced by
The method getPhoto() does not exist on yii\web\IdentityInterface. It seems like you code against a sub-type of yii\web\IdentityInterface such as craft\elements\User. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

138
            /** @scrutinizer ignore-call */ 
139
            $photo = $user->getPhoto();
Loading history...
139
            if ($photo !== null) {
140
                $photoUrl = $photo->getUrl();
141
            }
142
            // Fill in the initial data
143
            $data = new SsoData([
144
                'uniqueid' => $user->id,
145
                'name' => $name,
146
                'email' => $user->email,
147
                'photourl' => $photoUrl,
148
            ]);
149
        }
150
        // Give plugins a chance to modify it
151
        $event = new SsoDataEvent([
152
            'user' => $user,
153
            'ssoData' => $data,
154
        ]);
155
        $this->trigger(self::EVENT_SSO_DATA, $event);
156
        if (!$event->isValid) {
157
            return null;
158
        }
159
160
        return $event->ssoData;
161
    }
162
163
    /**
164
     * Get the plugin's settings, parsing any environment variables
165
     *
166
     * @return Settings
167
     */
168
    private function getPluginSettings(): Settings
169
    {
170
        /** @var Settings $settings */
171
        $settings = Vanillaforums::$plugin->getSettings();
172
        if (Vanillaforums::$craft31) {
173
            $settings->vanillaForumsClientID = Craft::parseEnv($settings->vanillaForumsClientID);
0 ignored issues
show
Documentation Bug introduced by
It seems like Craft::parseEnv($settings->vanillaForumsClientID) can also be of type boolean. However, the property $vanillaForumsClientID is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
174
            $settings->vanillaForumsSecret = Craft::parseEnv($settings->vanillaForumsSecret);
0 ignored issues
show
Documentation Bug introduced by
It seems like Craft::parseEnv($settings->vanillaForumsSecret) can also be of type boolean. However, the property $vanillaForumsSecret is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
175
        }
176
177
        return $settings;
178
    }
179
}
180