-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathasu_rest.install
More file actions
202 lines (184 loc) · 6.67 KB
/
asu_rest.install
File metadata and controls
202 lines (184 loc) · 6.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
<?php
/**
* @file
* Install, update and uninstall functions for the asu_rest module.
*/
declare(strict_types=1);
use Drupal\consumers\Entity\Consumer;
/**
* Implements hook_install().
*/
function asu_rest_install(): void {
asu_rest_provision_oauth_consumer();
}
/**
* Creates the machine OAuth consumer for REST clients.
*
* One-time for existing sites.
*/
function asu_rest_update_10001(): void {
asu_rest_provision_oauth_consumer();
}
/**
* Ensures the configured OAuth consumer exists when secrets are available.
*
* Consumers are content entities and cannot be shipped as config YAML. This
* provisions the client expected by apartment-application-service when
* ASU_REST_OAUTH_CLIENT_SECRET is set in the environment (and optionally
* ASU_REST_OAUTH_CLIENT_ID). Client secret is never stored in config.
*/
function asu_rest_provision_oauth_consumer(): void {
if (!\Drupal::moduleHandler()->moduleExists('simple_oauth')) {
return;
}
$config = \Drupal::configFactory()->get('asu_rest.settings');
$oauth = $config->get('oauth_consumer') ?? [];
// Default TRUE so provisioning still runs if hook_install runs before CMI
// import.
$auto_create = $oauth['auto_create'] ?? TRUE;
if (!$auto_create) {
return;
}
$client_id = $oauth['client_id'] ?? 'apartment_application_service';
$client_id = is_string($client_id) ? trim($client_id) : '';
if ($client_id === '') {
\Drupal::logger('asu_rest')->warning('OAuth consumer auto-create is enabled but client_id is empty; skipping.');
return;
}
$secret = getenv('ASU_REST_OAUTH_CLIENT_SECRET');
if ($secret === FALSE || $secret === '') {
\Drupal::logger('asu_rest')->notice(
'ASU_REST_OAUTH_CLIENT_SECRET is not set; skipping automatic OAuth consumer creation for client ID @id.',
['@id' => $client_id],
);
return;
}
$env_client_id = getenv('ASU_REST_OAUTH_CLIENT_ID');
if (is_string($env_client_id) && $env_client_id !== '') {
$client_id = trim($env_client_id);
}
$storage = \Drupal::entityTypeManager()->getStorage('consumer');
$existing_ids = $storage->getQuery()
->accessCheck(FALSE)
->condition('client_id', $client_id)
->range(0, 1)
->execute();
if ($existing_ids) {
// Ensure default user exists and is assigned, even if consumer already
// exists (common when secrets are added after initial install).
$role_storage = \Drupal::entityTypeManager()->getStorage('user_role');
if (!$role_storage->load('rest_client')) {
\Drupal::logger('asu_rest')->error(
'User role rest_client is missing; cannot provision OAuth REST client user for consumer @id.',
['@id' => $client_id],
);
return;
}
$username_env = getenv('ASU_REST_OAUTH_DEFAULT_USERNAME');
$username = is_string($username_env) && trim($username_env) !== '' ? trim($username_env) : 'rest_client';
$user_storage = \Drupal::entityTypeManager()->getStorage('user');
$users = $user_storage->loadByProperties(['name' => $username]);
/** @var \Drupal\user\Entity\User|null $user */
$user = $users ? reset($users) : NULL;
if (!$user) {
$password = bin2hex(random_bytes(16));
$user = $user_storage->create([
'name' => $username,
'status' => 1,
'roles' => ['rest_client'],
'pass' => $password,
]);
$user->save();
}
else {
if (!$user->isActive()) {
$user->activate();
}
if (!in_array('rest_client', $user->getRoles(), TRUE)) {
$user->addRole('rest_client');
}
$user->save();
}
/** @var \Drupal\consumers\Entity\Consumer|null $existing_consumer */
$existing_consumer = $storage->load(reset($existing_ids));
if ($existing_consumer && $existing_consumer->get('user_id')->isEmpty()) {
$existing_consumer->set('user_id', (int) $user->id())->save();
\Drupal::logger('asu_rest')->notice(
'Updated OAuth consumer @id default user to uid @uid.',
['@id' => $client_id, '@uid' => $user->id()],
);
}
return;
}
$entity_type_manager = \Drupal::entityTypeManager();
// Some environments provide scopes as config entities (oauth2_scope). If the
// entity type exists, ensure the expected scope is present.
if ($entity_type_manager->hasDefinition('oauth2_scope')) {
$scope_storage = $entity_type_manager->getStorage('oauth2_scope');
if (!$scope_storage->load('rest_client')) {
\Drupal::logger('asu_rest')->error(
'OAuth scope rest_client is missing; cannot create consumer @id.',
['@id' => $client_id],
);
return;
}
}
$role_storage = \Drupal::entityTypeManager()->getStorage('user_role');
if (!$role_storage->load('rest_client')) {
\Drupal::logger('asu_rest')->error(
'User role rest_client is missing; cannot provision OAuth REST client user for consumer @id.',
['@id' => $client_id],
);
return;
}
$username_env = getenv('ASU_REST_OAUTH_DEFAULT_USERNAME');
$username = is_string($username_env) && trim($username_env) !== '' ? trim($username_env) : 'rest_client';
$user_storage = \Drupal::entityTypeManager()->getStorage('user');
$users = $user_storage->loadByProperties(['name' => $username]);
/** @var \Drupal\user\Entity\User|null $user */
$user = $users ? reset($users) : NULL;
if (!$user) {
$password = bin2hex(random_bytes(16));
$user = $user_storage->create([
'name' => $username,
'status' => 1,
'roles' => ['rest_client'],
'pass' => $password,
]);
$user->save();
\Drupal::logger('asu_rest')->notice(
'Created OAuth REST client user @name (uid @uid) for consumer @id.',
['@name' => $username, '@uid' => $user->id(), '@id' => $client_id],
);
}
else {
if (!$user->isActive()) {
$user->activate();
}
if (!in_array('rest_client', $user->getRoles(), TRUE)) {
$user->addRole('rest_client');
}
$user->save();
}
$label = $oauth['label'] ?? 'Apartment application service';
$label = is_string($label) && $label !== '' ? $label : 'Apartment application service';
$consumer = Consumer::create([
'client_id' => $client_id,
'label' => $label,
'description' => 'Machine client for Elasticsearch-compatible REST endpoints (projects, apartments).',
// simple_oauth implements OAuth2 scopes as Drupal user roles on the
// consumer entity.
'roles' => ['rest_client'],
'user_id' => (int) $user->id(),
'confidential' => TRUE,
'secret' => $secret,
'redirect' => 'https://127.0.0.1/oauth2-callback-not-used',
'third_party' => FALSE,
'is_default' => FALSE,
]);
$consumer->save();
\Drupal::logger('asu_rest')->notice(
'Created OAuth consumer @id for REST API client credentials.',
['@id' => $client_id],
);
}