1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace rhertogh\Yii2Oauth2Server\controllers\console\client; |
4
|
|
|
|
5
|
|
|
use rhertogh\Yii2Oauth2Server\controllers\console\client\base\Oauth2BaseClientAction; |
6
|
|
|
use rhertogh\Yii2Oauth2Server\controllers\console\Oauth2ClientController; |
7
|
|
|
use rhertogh\Yii2Oauth2Server\helpers\DiHelper; |
8
|
|
|
use rhertogh\Yii2Oauth2Server\interfaces\controllers\console\client\Oauth2ViewClientActionInterface; |
9
|
|
|
use rhertogh\Yii2Oauth2Server\interfaces\models\Oauth2ClientScopeInterface; |
10
|
|
|
use rhertogh\Yii2Oauth2Server\interfaces\models\Oauth2ScopeInterface; |
11
|
|
|
use rhertogh\Yii2Oauth2Server\Oauth2Module; |
12
|
|
|
use Throwable; |
13
|
|
|
use yii\base\InvalidConfigException; |
14
|
|
|
use yii\console\Exception as ConsoleException; |
15
|
|
|
use yii\console\ExitCode; |
16
|
|
|
use yii\console\widgets\Table; |
17
|
|
|
use yii\helpers\Console; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @property Oauth2ClientController $controller |
21
|
|
|
*/ |
22
|
|
|
class Oauth2ViewClientAction extends Oauth2BaseClientAction implements Oauth2ViewClientActionInterface |
23
|
|
|
{ |
24
|
|
|
/** |
25
|
|
|
* View configuration details of an Oauth2 Client. |
26
|
|
|
* |
27
|
|
|
* @throws Throwable |
28
|
|
|
* @throws InvalidConfigException |
29
|
|
|
* @throws ConsoleException |
30
|
|
|
*/ |
31
|
|
|
public function run($id) |
32
|
|
|
{ |
33
|
|
|
$module = $this->controller->module; |
34
|
|
|
$client = $this->findByIdOrIdentifier($id); |
35
|
|
|
|
36
|
|
|
$clientInfo = [ |
37
|
|
|
'ID' => $client->getPrimaryKey(), |
38
|
|
|
'Identifier' => $client->getIdentifier(), |
39
|
|
|
'Name' => $client->getName(), |
40
|
|
|
'Type' => $client->isConfidential() |
41
|
|
|
? 'Confidential (client secret required)' |
42
|
|
|
: 'Public (no client secret required)', |
43
|
|
|
'Redirect URIs' => implode(', ', $client->getRedirectUri()), |
44
|
|
|
'Allow Variable URI Query' => $client->isVariableRedirectUriQueryAllowed() ? 'Yes' : 'No', |
45
|
|
|
'Grant Types' => implode(',', Oauth2Module::getGrantTypeIdentifiers($client->getGrantTypes())), |
46
|
|
|
'Allow Generic Scopes' => $client->getAllowGenericScopes() ? 'Yes' : 'No', |
47
|
|
|
'Throw Exception on invalid Scope' => $client->getExceptionOnInvalidScope() !== null |
48
|
|
|
? ($client->getExceptionOnInvalidScope() ? 'Yes' : 'No') |
49
|
|
|
: '[Not set, using module: ' . ($module->exceptionOnInvalidScope ? 'Yes' : 'No') . ']', |
50
|
|
|
'End Users may authorize client' => $client->endUsersMayAuthorizeClient() ? 'Yes' : 'No', |
51
|
|
|
'User Account Selection' => $client->getUserAccountSelection() !== null |
52
|
|
|
? Oauth2Module::USER_ACCOUNT_SELECTION_NAMES[$client->getUserAccountSelection()] |
53
|
|
|
: '[Not set, using module: ' |
54
|
|
|
. Oauth2Module::USER_ACCOUNT_SELECTION_NAMES[$module->defaultUserAccountSelection] |
55
|
|
|
. ']', |
56
|
|
|
'Is Auth Code without PKCE allowed' => $client->isAuthCodeWithoutPkceAllowed() ? 'Yes' : 'No', |
57
|
|
|
'Skip authorization if scope is allowed' => $client->skipAuthorizationIfScopeIsAllowed() ? 'Yes' : 'No', |
58
|
|
|
'Logo URI' => $client->getLogoUri(), |
59
|
|
|
'Terms of Service URI' => $client->getTermsOfServiceUri(), |
60
|
|
|
'Contacts' => $client->getContacts(), |
61
|
|
|
]; |
62
|
|
|
|
63
|
|
|
if ($client->validateGrantType(Oauth2Module::GRANT_TYPE_IDENTIFIER_CLIENT_CREDENTIALS)) { |
64
|
|
|
$clientInfo['Client Credentials Grant User'] = $client->getClientCredentialsGrantUserId(); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
if ($module->enableOpenIdConnect) { |
68
|
|
|
$clientInfo['OIDC allow offline access without consent'] = |
69
|
|
|
$client->getOpenIdConnectAllowOfflineAccessWithoutConsent() ? 'Yes' : 'No'; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
$clientInfo['Enabled'] = $client->isEnabled() ? 'Yes' : 'No'; |
73
|
|
|
|
74
|
|
|
$this->controller->stdout(Table::widget([ |
75
|
|
|
'rows' => array_map(fn($property) => [$property, $clientInfo[$property]], array_keys($clientInfo)), |
76
|
|
|
])); |
77
|
|
|
|
78
|
|
|
$scopes = $client->getAllowedScopes(true); |
79
|
|
|
|
80
|
|
|
if ($this->controller->verbose) { |
81
|
|
|
$this->controller->stdout(PHP_EOL); |
82
|
|
|
|
83
|
|
|
usort( |
84
|
|
|
$scopes, |
85
|
|
|
fn(Oauth2ScopeInterface $a, Oauth2ScopeInterface $b) => strcmp($a->getIdentifier(), $b->getIdentifier()) |
86
|
|
|
); |
87
|
|
|
|
88
|
|
|
$scopeInfo = []; |
89
|
|
|
$showInheritedInfo = false; |
90
|
|
|
foreach ($scopes as $scope) { |
91
|
|
|
$clientScope = $scope->getClientScope($client->getPrimaryKey()); |
92
|
|
|
$appliedByDefault = null; |
93
|
|
|
$requiredOnAuthorization = null; |
94
|
|
|
if ($clientScope) { |
95
|
|
|
if ($clientScope->getAppliedByDefault() !== null) { |
96
|
|
|
$appliedByDefault = $this->generateAppliedByDefaultLabel($clientScope->getAppliedByDefault()); |
97
|
|
|
} |
98
|
|
|
if ($clientScope->getRequiredOnAuthorization() !== null) { |
99
|
|
|
$requiredOnAuthorization = ($clientScope->getRequiredOnAuthorization() ? 'Yes' : 'No'); |
100
|
|
|
} |
101
|
|
|
} |
102
|
|
|
if (empty($appliedByDefault)) { |
103
|
|
|
$appliedByDefault = $this->generateAppliedByDefaultLabel($scope->getAppliedByDefault()) . '¹'; |
104
|
|
|
$showInheritedInfo = true; |
105
|
|
|
} |
106
|
|
|
if (empty($requiredOnAuthorization)) { |
107
|
|
|
$requiredOnAuthorization = ($scope->getRequiredOnAuthorization() ? 'Yes' : 'No') . '¹'; |
108
|
|
|
$showInheritedInfo = true; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
$scopeInfo[] = [ |
112
|
|
|
$scope->getIdentifier(), |
113
|
|
|
$appliedByDefault, |
114
|
|
|
$requiredOnAuthorization, |
115
|
|
|
$clientScope ? 'Client' : 'Generic', |
116
|
|
|
]; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
$this->controller->stdout(count($scopes) . ' scope(s) configured for "' |
120
|
|
|
. $client->getIdentifier() . '" client using ' |
121
|
|
|
. ($client->getAllowGenericScopes() |
122
|
|
|
? 'generic mode (scopes don\'t need to be explicitly configured for this client)' |
123
|
|
|
: 'strict mode (scopes need to be explicitly configured for this client)' |
124
|
|
|
) |
125
|
|
|
. '.' . PHP_EOL); |
126
|
|
|
|
127
|
|
|
$this->controller->stdout(Table::widget([ |
128
|
|
|
'headers' => [ |
129
|
|
|
'Scope', |
130
|
|
|
'Applied by default', |
131
|
|
|
'Required on authorization', |
132
|
|
|
'Origin', |
133
|
|
|
], |
134
|
|
|
'rows' => $scopeInfo, |
135
|
|
|
])); |
136
|
|
|
|
137
|
|
|
if ($showInheritedInfo) { |
138
|
|
|
$this->controller->stdout('¹ Config inherited from scope.' . PHP_EOL); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** @var Oauth2ClientScopeInterface $clientScopeClass */ |
142
|
|
|
$clientScopeClass = DiHelper::getValidatedClassName(Oauth2ClientScopeInterface::class); |
143
|
|
|
$clientScopeTableName = $clientScopeClass::tableName(); |
144
|
|
|
$disabledClientScopes = $clientScopeClass::find() |
145
|
|
|
->innerJoinWith('scope') |
146
|
|
|
->andWhere([$clientScopeTableName . '.client_id' => $client->getPrimaryKey()]) |
147
|
|
|
->andWhere([$clientScopeTableName . '.enabled' => false]) |
148
|
|
|
->select('identifier') |
149
|
|
|
->column(); |
150
|
|
|
|
151
|
|
|
if ($disabledClientScopes) { |
|
|
|
|
152
|
|
|
$this->controller->stdout('Note: there is/are ' . count($disabledClientScopes) |
153
|
|
|
. ' scope(s) explicitly disabled for this client: ' . implode(', ', $disabledClientScopes) |
154
|
|
|
. '.' . PHP_EOL); |
155
|
|
|
} |
156
|
|
|
} else { |
157
|
|
|
$this->controller->stdout(count($scopes) . ' scope(s) configured for "' |
158
|
|
|
. $client->getIdentifier() . '" client.' . PHP_EOL); |
159
|
|
|
|
160
|
|
|
if ($scopes) { |
|
|
|
|
161
|
|
|
$this->controller->stdout('Hint: use `--verbose` to show scope configuration.' |
162
|
|
|
. PHP_EOL, Console::ITALIC); |
163
|
|
|
} |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
return ExitCode::OK; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
protected function generateAppliedByDefaultLabel($appliedByDefault) |
170
|
|
|
{ |
171
|
|
|
switch ($appliedByDefault) { |
172
|
|
|
case Oauth2ScopeInterface::APPLIED_BY_DEFAULT_NO: |
173
|
|
|
return 'No'; |
174
|
|
|
case Oauth2ScopeInterface::APPLIED_BY_DEFAULT_CONFIRM: |
175
|
|
|
return 'Yes (with user confirm)'; |
176
|
|
|
case Oauth2ScopeInterface::APPLIED_BY_DEFAULT_AUTOMATICALLY: |
177
|
|
|
return 'Yes (w/o user confirm)'; |
178
|
|
|
case Oauth2ScopeInterface::APPLIED_BY_DEFAULT_IF_REQUESTED: |
179
|
|
|
return 'Yes (upon client request w/o user confirm)'; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
throw new \LogicException('Unknown "applied by default" value: ' . $appliedByDefault); |
183
|
|
|
} |
184
|
|
|
} |
185
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.