ApiContainer::behaviors()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 14
nc 1
nop 0
dl 0
loc 18
ccs 3
cts 3
cp 1
crap 1
rs 9.7998
c 0
b 0
f 0
1
<?php
2
3
namespace roaresearch\yii2\roa\modules;
4
5
use roaresearch\yii2\oauth2server\filters\auth\CompositeAuth;
6
use roaresearch\yii2\oauth2server\Module as OAuth2Module;
7
use roaresearch\yii2\roa\{
8
    controllers\ApiContainerController,
9
    urlRules\Composite as CompositeUrlRule,
10
    urlRules\Modular as ModularUrlRule,
11
    urlRules\UrlRuleCreator
12
};
13
use Yii;
14
use yii\{
15
    base\BootstrapInterface,
16
    base\InvalidArgumentException,
17
    base\Module,
18
    filters\ContentNegotiator,
19
    helpers\Url,
20
    web\Response,
21
    web\UrlNormalizer
22
};
23
24
/**
25
 * @author Angel (Faryshta) Guevara <[email protected]>
26
 *
27
 * @var OAuth2Module $oauth2Module
28
 */
29
class ApiContainer extends Module implements UrlRuleCreator, BootstrapInterface
30
{
31
    /**
32
     * @var string
33
     */
34
    public string $identityClass;
35
36
    /**
37
     * @var string
38
     */
39
    public string $versionUrlRuleClass = ModularUrlRule::class;
40
41
    /**
42
     * @var string
43
     */
44
    public string $containerUrlRuleClass = ModularUrlRule::class;
45
46
    /**
47
     * @inheritdoc
48
     */
49
    public $defaultRoute = 'index';
50
51
    /**
52
     * @inheritdoc
53
     */
54
    public $controllerMap = ['index' => ApiContainerController::class];
55
56
    /**
57
     * @var array
58
     */
59
    public array $versions = [];
60
61
    /**
62
     * @var string
63
     */
64
    public string $errorAction;
65
66
    /**
67
     * @var string the module id for the oauth2 server module.
68
     */
69
    public string $oauth2ModuleId = 'oauth2';
70
71
    /**
72
     * @var array default OAuth2Module configuration.
73
     */
74
    private array $oauth2Module = [
75
        'class' => OAuth2Module::class,
76
        'tokenParamName' => 'accessToken',
77
        'tokenAccessLifetime' => 3600 * 24,
78
        'storageMap' => [
79
        ],
80
        'grantTypes' => [
81
            'user_credentials' => [
82
                'class' => \OAuth2\GrantType\UserCredentials::class,
83
            ],
84
            'refresh_token' => [
85
                'class' => \OAuth2\GrantType\RefreshToken::class,
86
                'always_issue_new_refresh_token' => true
87
            ],
88
        ],
89
    ];
90
91
    /**
92
     * @inheritdoc
93
     */
94 21
    public function behaviors()
95
    {
96
        return [
97
            'contentNegotiator' => [
98
                'class' => ContentNegotiator::class,
99
                'formats' => [
100
                    'application/json' => Response::FORMAT_JSON,
101
                    'application/xml' => Response::FORMAT_XML,
102
                ],
103
            ],
104
            'authenticator' => [
105
                'class' => CompositeAuth::class,
106 21
                'oauth2Module' => $this->getUniqueId() . '/'
107 21
                    . $this->oauth2ModuleId,
108
                'except' => [
109
                    'oauth2/*', // the oauth2 module
110
                    'index/*', // controller that return this module info
111
                    '*/options', // all OPTIONS actions for CORS preflight
112
                ],
113
            ],
114
        ];
115
    }
116
117
    /**
118
     * @var array module
119
     */
120
    public function setOauth2Module($module)
121
    {
122
        if (is_array($module)) {
123
            $this->setModule($this->oauth2ModuleId, array_merge(
124
                $this->oauth2Module,
125
                ['storageMap' => ['user_credentials' => $this->identityClass]],
126
                $module
127
            ));
128
        } elseif (!$module instanceof OAuth2Module) {
129
            $this->setModule($this->oauth2ModuleId, $module);
130
        } else {
131
            throw new InvalidArgumentException(
132
                static::class
133
                    . '::$oauth2Module must be an array or instance of '
134
                    . OAuth2Module::class
135
            );
136
        }
137
    }
138
139
    /**
140
     * @return OAuth2Module
141
     */
142
    public function getOauth2Module(): OAuth2Module
143
    {
144
        if (!$this->hasModule($this->oauth2ModuleId)) {
145
            $this->oauth2Module['storageMap']['user_credentials']
146
                = $this->identityClass;
147
            $this->setModule($this->oauth2ModuleId, $this->oauth2Module);
148
        }
149
150
        return $this->getModule($this->oauth2ModuleId);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getModule($this->oauth2ModuleId) returns the type null which is incompatible with the type-hinted return roaresearch\yii2\oauth2server\Module.
Loading history...
151
    }
152
153
    /**
154
     * @inheritdoc
155
     */
156
    public function bootstrap($app)
157
    {
158
        $this->getOauth2Module()->bootstrap($app);
159
        if (empty($this->errorAction)) {
160
            $this->errorAction = $this->uniqueId . '/index/error';
161
        }
162
        $app->urlManager->addRules([[
163
            'class' => $this->containerUrlRuleClass,
164
            'moduleId' => $this->uniqueId,
165
            'normalizer' => [
166
                'action' => UrlNormalizer::ACTION_REDIRECT_PERMANENT,
167
            ],
168
        ]]);
169
    }
170
171
    /**
172
     * @return ApiVersion[] return all the versions attached to the container
173
     * indexed by their respective id.
174
     */
175 1
    public function getVersionModules(): array
176
    {
177 1
        $versions = [];
178 1
        foreach ($this->versions as $route => $config) {
179 1
            if (!$this->hasModule($route)) {
180
                $this->setModule($route, $config);
181
            }
182 1
            $versions[$route] = $this->getModule($route);
183
        }
184
185 1
        return $versions;
186
    }
187
188
    /**
189
     * @return \yii\web\UrlRuleInterface[]
190
     */
191 21
    protected function defaultUrlRules(): array
192
    {
193
        return [
194 21
            Yii::createObject([
195
                'class' => \yii\web\UrlRule::class,
196 21
                'pattern' => $this->getUniqueId(),
197 21
                'route' => $this->getUniqueId(),
198
            ]),
199
        ];
200
    }
201
202
    /**
203
     * @inheritdoc
204
     */
205 21
    public function initCreator(CompositeUrlRule $urlRule): void
206
    {
207
        // change the error handler and identityClass
208 21
        Yii::$app->errorHandler->errorAction = $this->errorAction;
209 21
        Yii::$app->user->identityClass = $this->identityClass;
210
211 21
        $auth = $this->getBehavior('authenticator');
212 21
        foreach ($this->versions as $route => $config) {
213 21
            $auth->except[] = $route . '/index/*';
214 21
            $this->setModule($route, $config);
215
        }
216
    }
217
218
    /**
219
     * @inheritdoc
220
     */
221 21
    public function createUrlRules(CompositeUrlRule $urlRule): array
222
    {
223 21
        $rules = $this->defaultUrlRules();
224 21
        foreach ($this->versions as $route => $config) {
225 21
            $rules[] = Yii::createObject([
226 21
                'class' => $this->versionUrlRuleClass,
227 21
                'moduleId' => "{$this->uniqueId}/$route",
228
            ]);
229
        }
230
231 21
        return $rules;
232
    }
233
234
    /**
235
     * @return string HTTP Url linking to this module
236
     */
237
    public function getSelfLink(): string
238
    {
239
        return Url::to(['//' . $this->getUniqueId()]);
240
    }
241
}
242