-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathhosting_restapi.site.inc
283 lines (237 loc) · 8.69 KB
/
hosting_restapi.site.inc
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
<?php
/**
* @file
* Hosting REST API functions, and Drupal hooks.
*/
/**
* Implements the 'site' API.
*/
function hosting_restapi_site() {
hosting_restapi_cors_headers();
$method = strtolower($_SERVER['REQUEST_METHOD']);
$f = 'hosting_restapi_site_' . $method;
try {
if (function_exists($f)) {
$result = $f();
}
else {
$result = array(
'status' => 'error',
'message' => 'Unknown method for site: ' . $_SERVER['REQUEST_METHOD'],
);
}
}
catch (Exception $e) {
$result = array(
'status' => 'error',
'message' => $e->getMessage(),
);
}
echo json_encode($result);
drupal_exit();
}
/**
* Implements the 'site GET' API (get info/status).
*/
function hosting_restapi_site_get() {
// FIXME: we want the user to have live updates on the status of the site,
// so either we grant some temporary access, or we grant to anons.
$access = hosting_restapi_check_access($_GET['key'], $_GET['secret']);
if (!$access) {
throw new Exception(t('Access denied.'));
}
$url = $_GET['url'];
$invoice_id = (isset($_GET['invoice']) ? $_GET['invoice'] : NULL);
if (! $url) {
throw new Exception(t('The "url" parameter was empty.'));
}
// If we had the invoice_id, return the site status
// NB: as a security measure, the invoice_id and url must match.
if ($invoice_id) {
$order_id = db_query('SELECT id FROM hosting_restapi_order WHERE invoice_id = :invoice_id AND site = :url', array(':invoice_id' => $invoice_id, ':url' => $url))->fetchField();
$site = hosting_get_site_by_url($url);
if (! $order_id) {
throw new Exception(t('Invalid invoice ID or URL. Please contact support for more information.'));
}
// Fetch the last log.
$result = db_query('SELECT * FROM hosting_restapi_log WHERE order_id = :order_id ORDER BY created DESC limit 1', array(':order_id' => $order_id));
if ($record = $result->fetchObject()) {
// If it's a new order, check if the cloning has begun.
if ($record->task == HOSTING_RESTAPI_ORDER_STATUS_NEW) {
// FIXME HARDCODE: 608 = clone task of demo.s.c
$vid = db_query("SELECT max(vid) from hosting_task_arguments where nid = 608 and name = 'new_uri' and value = :url", array(':url' => $url))->fetchField();
$clone_status = db_query("SELECT task_status FROM hosting_task WHERE vid = :vid", array(':vid' => $vid))->fetchField();
if ($clone_status == HOSTING_TASK_PROCESSING) {
$result = array(
'status' => 'success',
'data' => array(
'site_url' => $site->title,
'site_status' => HOSTING_RESTAPI_ORDER_STATUS_CLONE_INPROGRESS,
),
);
return $result;
}
elseif ($clone_status == HOSTING_TASK_ERROR) {
$result = array(
'status' => 'error',
'data' => array(
'site_url' => $site->title,
'message' => "Clone operation failed.",
),
);
return $result;
}
}
elseif ($record->task == HOSTING_RESTAPI_ORDER_STATUS_VERIFY_COMPLETE) {
$result = array(
'status' => 'success',
'data' => array(
'site_url' => $site->title,
'site_status' => $record->task,
'login_link' => 'https://' . $site->title . '/user',
),
);
$cache = cache_get("hosting:site:" . $site->nid . ":symbiotic_login");
if ($cache && (time() < $cache->data['expire'])) {
$result['data']['login_link'] = $cache->data['link'];
}
return $result;
}
$result = array(
'status' => 'success',
'data' => array(
'site_url' => $site->title,
'site_status' => $record->task,
),
);
return $result;
}
}
// Otherwise just return if the site exists or not.
$site = hosting_get_site_by_url($url);
if (! $site) {
throw new Exception(t('Site not found. Please contact support for more information.'));
}
$result = array(
'status' => 'success',
'data' => array(
'site_url' => $site->title,
'site_status' => $site->site_status,
),
);
return $result;
}
/**
* Implements the 'site POST' API (create).
*
* NB: permission to create a site is done by checking the validity
* of the invoice ($_POST['invoice']) in CiviCRM.
* See @hosting_restapi_is_valid_invoice().
*
* The main domain ([url].example.org) is now enforced by hosting_saas, so
* we only handle the [url] is rather the subdomain.
*/
function hosting_restapi_site_post() {
$access = hosting_restapi_check_access($_POST['key'], $_POST['secret']);
if (!$access) {
throw new Exception(t('Access denied.'));
}
// TODO : check if URL format is OK (i.e. no spaces, etc)
$url = check_plain($_POST['url']);
$url = preg_replace('/[^\.a-z0-9]/', '', $url);
if ($url != $_POST['url']) {
throw new Exception('The "url" parameter had invalid characters.');
}
$site = _hosting_restapi_is_allowed_creation($url);
// Check if the order/invoice already exists.
$invoice_id = $_POST['invoice'];
hosting_restapi_is_valid_invoice($invoice_id);
// Log order in hosting_restapi_order.
$record = new StdClass();
$record->invoice_id = $invoice_id;
$record->site = $url;
$record->token = sha1($url . uniqid(mt_rand()));
$record->ip = $_SERVER['REMOTE_ADDR'];
$record->crmhost = $_POST['crmhost'] ?? '';
$record->current_status = HOSTING_RESTAPI_ORDER_STATUS_NEW;
$record->created = time();
$record->updated = time();
drupal_write_record('hosting_restapi_order', $record);
// Clone a site
$template_nid = variable_get('hosting_saas_template_site_nid', NULL);
$new_platform_nid = variable_get('hosting_saas_target_platform', NULL);
$new_db_server = variable_get('hosting_saas_db_server', 2); // 2 = DB server on single-server default setups
if (hosting_restapi_check_capacity() === FALSE) {
throw new Exception('Service has reached max capacity.');
}
$result = hosting_add_task($template_nid, 'clone', [
'new_uri' => $url,
'new_db_server' => $new_db_server,
'target_platform' => $new_platform_nid,
'aliases' => '',
'redirection' => '',
// This is mostly symbolic and not used at the moment (aegir hooks are a pain)
'hosting_restapi_token' => $record->token,
// @todo Use the setting from the template site?
// although there are few reasons not to require https
'https_enabled' => 2,
]);
hosting_restapi_log($url, HOSTING_RESTAPI_ORDER_STATUS_NEW, t('The order is valid and has been created.'));
return array('status' => 'success', 'data' => $result);
}
/**
* A few quick pre-flight checks to make sure that the system
* is correctly configured (Drupal variales) and that the URL
* is valid.
*/
function _hosting_restapi_is_allowed_creation($url) {
if (!$url) {
throw new Exception('The "url" parameter was empty.');
}
// This function checks for uniqueness of domains and aliases.
// TODO: we should also implement hook_allow_domain() to enforce domain?
if (! hosting_domain_allowed($url)) {
throw new Exception('The url is not allowed by local configurations: ' . $url);
}
$site = hosting_get_site_by_url($url);
if ($site) {
throw new Exception('The site already exists.');
}
$variables = array(
'hosting_saas_target_platform',
'hosting_saas_db_server',
'hosting_saas_template_site_nid',
);
foreach ($variables as $to_check) {
if (variable_get($to_check, NULL) === NULL) {
throw new Exception("Variable hasn't been chosen yet: " . $to_check);
}
}
return $site;
}
/**
* Checks if an invoice ID is valid (not already used, and valid in the transactional site).
* i.e. checks if the invoice_id is valid in CiviCRM.
*
* @param String $invoice_id
* @returns Boolean
*/
function hosting_restapi_is_valid_invoice($invoice_id) {
if (! $invoice_id) {
throw new Exception('Missing invoice.');
}
// Check if the invoice_id was already used.
// XXX assumes that 1 invoice = 1 site. We don't support multi-quota stuff,
// since we don't give access to Aegir.
$exists = db_query('SELECT count(*) as cpt FROM hosting_restapi_order WHERE invoice_id = :invoice_id', array(':invoice_id' => $invoice_id))->fetchField();
if ($exists) {
watchdog('hosting_restapi', 'Invoice ID already used.');
throw new Exception('Invalid invoice ID.');
}
// NB: for now, we assume that hook implementations will throw an exception
// if they couldn't validate the invoice_id. It assumes we have only one
// way to validate invoices (create orders). If we want to change this at
// some point, we should add a "invoice_type" argument.
module_invoke_all("hosting_restapi_validate_invoice_id", $invoice_id);
return TRUE;
}