blob: f99c86db42641785f2e295bbd616518e6489f87c [file] [log] [blame]
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001<?php
2require_once $_SERVER['DOCUMENT_ROOT'] . '/modals/footer.php';
3logger();
4
5$hash = $js_minifier->getDataHash();
6$JSPath = '/tmp/' . $hash . '.js';
7if(!file_exists($JSPath)) {
8 $js_minifier->minify($JSPath);
9 cleanupJS($hash);
10}
11?>
12<script src="/cache/<?=basename($JSPath)?>"></script>
13<script>
14<?php
15$lang_footer = json_encode($lang['footer']);
16$lang_acl = json_encode($lang['acl']);
17$lang_tfa = json_encode($lang['tfa']);
18$lang_fido2 = json_encode($lang['fido2']);
19echo "var lang_footer = ". $lang_footer . ";\n";
20echo "var lang_acl = ". $lang_acl . ";\n";
21echo "var lang_tfa = ". $lang_tfa . ";\n";
22echo "var lang_fido2 = ". $lang_fido2 . ";\n";
23echo "var docker_timeout = ". $DOCKER_TIMEOUT * 1000 . ";\n";
24?>
25$(window).scroll(function() {
26 sessionStorage.scrollTop = $(this).scrollTop();
27});
28// Select language and reopen active URL without POST
29function setLang(sel) {
30 $.post( "<?= $_SERVER['REQUEST_URI']; ?>", {lang: sel} );
31 window.location.href = window.location.pathname + window.location.search;
32}
33// FIDO2 functions
34function arrayBufferToBase64(buffer) {
35 let binary = '';
36 let bytes = new Uint8Array(buffer);
37 let len = bytes.byteLength;
38 for (let i = 0; i < len; i++) {
39 binary += String.fromCharCode( bytes[ i ] );
40 }
41 return window.btoa(binary);
42}
43function recursiveBase64StrToArrayBuffer(obj) {
44 let prefix = '=?BINARY?B?';
45 let suffix = '?=';
46 if (typeof obj === 'object') {
47 for (let key in obj) {
48 if (typeof obj[key] === 'string') {
49 let str = obj[key];
50 if (str.substring(0, prefix.length) === prefix && str.substring(str.length - suffix.length) === suffix) {
51 str = str.substring(prefix.length, str.length - suffix.length);
52 let binary_string = window.atob(str);
53 let len = binary_string.length;
54 let bytes = new Uint8Array(len);
55 for (let i = 0; i < len; i++) {
56 bytes[i] = binary_string.charCodeAt(i);
57 }
58 obj[key] = bytes.buffer;
59 }
60 } else {
61 recursiveBase64StrToArrayBuffer(obj[key]);
62 }
63 }
64 }
65}
66$(window).load(function() {
67 $(".overlay").hide();
68});
69$(document).ready(function() {
70 $(document).on('shown.bs.modal', function(e) {
71 modal_id = $(e.relatedTarget).data('target');
72 $(modal_id).attr("aria-hidden","false");
73 });
74 // TFA, CSRF, Alerts in footer.inc.php
75 // Other general functions in mailcow.js
76 <?php
77 $alertbox_log_parser = alertbox_log_parser($_SESSION);
78 if (is_array($alertbox_log_parser)) {
79 foreach($alertbox_log_parser as $log) {
80 $alerts[$log['type']][] = $log['msg'];
81 }
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020082 $alerts = array_filter(array_unique($alerts));
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010083 foreach($alerts as $alert_type => $alert_msg) {
84 ?>
85 mailcow_alert_box(<?=json_encode(implode('<hr class="alert-hr">', $alert_msg));?>, <?=$alert_type;?>);
86 <?php
87 }
88 unset($_SESSION['return']);
89 }
90 ?>
91 // Confirm TFA modal
92 <?php if (isset($_SESSION['pending_tfa_method'])):?>
93 $('#ConfirmTFAModal').modal({
94 backdrop: 'static',
95 keyboard: false
96 });
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020097 $('#u2f_status_auth').html('<p><i class="bi bi-arrow-repeat icon-spin"></i> ' + lang_tfa.init_u2f + '</p>');
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +010098 $('#ConfirmTFAModal').on('shown.bs.modal', function(){
99 $(this).find('input[name=token]').focus();
100 // If U2F
101 if(document.getElementById("u2f_auth_data") !== null) {
102 $.ajax({
103 type: "GET",
104 cache: false,
105 dataType: 'script',
106 url: "/api/v1/get/u2f-authentication/<?= (isset($_SESSION['pending_mailcow_cc_username'])) ? rawurlencode($_SESSION['pending_mailcow_cc_username']) : null; ?>",
107 complete: function(data){
108 $('#u2f_status_auth').html(lang_tfa.waiting_usb_auth);
109 data;
110 setTimeout(function() {
111 console.log("Ready to authenticate");
112 u2f.sign(appId, challenge, registeredKeys, function(data) {
113 var form = document.getElementById('u2f_auth_form');
114 var auth = document.getElementById('u2f_auth_data');
115 console.log("Authenticate callback", data);
116 auth.value = JSON.stringify(data);
117 form.submit();
118 });
119 }, 1000);
120 }
121 });
122 }
123 });
124 $('#ConfirmTFAModal').on('hidden.bs.modal', function(){
125 $.ajax({
126 type: "GET",
127 cache: false,
128 dataType: 'script',
129 url: '/inc/ajax/destroy_tfa_auth.php',
130 complete: function(data){
131 window.location = window.location.href.split("#")[0];
132 }
133 });
134 });
135 <?php endif; ?>
136 // Validate FIDO2
137 $("#fido2-login").click(function(){
138 $('#fido2-alerts').html();
139 if (!window.fetch || !navigator.credentials || !navigator.credentials.create) {
140 window.alert('Browser not supported.');
141 return;
142 }
143 window.fetch("/api/v1/get/fido2-get-args", {method:'GET',cache:'no-cache'}).then(function(response) {
144 return response.json();
145 }).then(function(json) {
146 if (json.success === false) {
147 throw new Error();
148 }
149 recursiveBase64StrToArrayBuffer(json);
150 return json;
151 }).then(function(getCredentialArgs) {
152 return navigator.credentials.get(getCredentialArgs);
153 }).then(function(cred) {
154 return {
155 id: cred.rawId ? arrayBufferToBase64(cred.rawId) : null,
156 clientDataJSON: cred.response.clientDataJSON ? arrayBufferToBase64(cred.response.clientDataJSON) : null,
157 authenticatorData: cred.response.authenticatorData ? arrayBufferToBase64(cred.response.authenticatorData) : null,
158 signature : cred.response.signature ? arrayBufferToBase64(cred.response.signature) : null
159 };
160 }).then(JSON.stringify).then(function(AuthenticatorAttestationResponse) {
161 return window.fetch("/api/v1/process/fido2-args", {method:'POST', body: AuthenticatorAttestationResponse, cache:'no-cache'});
162 }).then(function(response) {
163 return response.json();
164 }).then(function(json) {
165 if (json.success) {
166 window.location = window.location.href.split("#")[0];
167 } else {
168 throw new Error();
169 }
170 }).catch(function(err) {
171 if (typeof err.message === 'undefined') {
172 mailcow_alert_box(lang_fido2.fido2_validation_failed, "danger");
173 } else {
174 mailcow_alert_box(lang_fido2.fido2_validation_failed + ":<br><i>" + err.message + "</i>", "danger");
175 }
176 });
177 });
178 // Set TFA/FIDO2
179 $("#register-fido2").click(function(){
180 $("option:selected").prop("selected", false);
181 if (!window.fetch || !navigator.credentials || !navigator.credentials.create) {
182 window.alert('Browser not supported.');
183 return;
184 }
185 window.fetch("/api/v1/get/fido2-registration/<?= (isset($_SESSION['mailcow_cc_username'])) ? rawurlencode($_SESSION['mailcow_cc_username']) : null; ?>", {method:'GET',cache:'no-cache'}).then(function(response) {
186 return response.json();
187 }).then(function(json) {
188 if (json.success === false) {
189 throw new Error(json.msg);
190 }
191 recursiveBase64StrToArrayBuffer(json);
192 return json;
193 }).then(function(createCredentialArgs) {
194 console.log(createCredentialArgs);
195 return navigator.credentials.create(createCredentialArgs);
196 }).then(function(cred) {
197 return {
198 clientDataJSON: cred.response.clientDataJSON ? arrayBufferToBase64(cred.response.clientDataJSON) : null,
199 attestationObject: cred.response.attestationObject ? arrayBufferToBase64(cred.response.attestationObject) : null
200 };
201 }).then(JSON.stringify).then(function(AuthenticatorAttestationResponse) {
202 return window.fetch("/api/v1/add/fido2-registration", {method:'POST', body: AuthenticatorAttestationResponse, cache:'no-cache'});
203 }).then(function(response) {
204 return response.json();
205 }).then(function(json) {
206 if (json.success) {
207 window.location = window.location.href.split("#")[0];
208 } else {
209 throw new Error(json.msg);
210 }
211 }).catch(function(err) {
212 $('#fido2-alerts').html('<span class="text-danger"><b>' + err.message + '</b></span>');
213 });
214 });
215 $('#selectTFA').change(function () {
216 if ($(this).val() == "yubi_otp") {
217 $('#YubiOTPModal').modal('show');
218 $("option:selected").prop("selected", false);
219 }
220 if ($(this).val() == "totp") {
221 $('#TOTPModal').modal('show');
222 request_token = $('#tfa-qr-img').data('totp-secret');
223 $.ajax({
224 url: '/inc/ajax/qr_gen.php',
225 data: {
226 token: request_token,
227 },
228 }).done(function (result) {
229 $("#tfa-qr-img").attr("src", result);
230 });
231 $("option:selected").prop("selected", false);
232 }
233 if ($(this).val() == "u2f") {
234 $('#U2FModal').modal('show');
235 $("option:selected").prop("selected", false);
236 $("#start_u2f_register").click(function(){
237 $('#u2f_return_code').html('');
238 $('#u2f_return_code').hide();
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200239 $('#u2f_status_reg').html('<p><i class="bi bi-arrow-repeat icon-spin"></i> ' + lang_tfa.init_u2f + '</p>');
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100240 $.ajax({
241 type: "GET",
242 cache: false,
243 dataType: 'script',
244 url: "/api/v1/get/u2f-registration/<?= (isset($_SESSION['mailcow_cc_username'])) ? rawurlencode($_SESSION['mailcow_cc_username']) : null; ?>",
245 complete: function(data){
246 data;
247 setTimeout(function() {
248 console.log("Ready to register");
249 $('#u2f_status_reg').html(lang_tfa.waiting_usb_register);
250 u2f.register(appId, registerRequests, registeredKeys, function(deviceResponse) {
251 var form = document.getElementById('u2f_reg_form');
252 var reg = document.getElementById('u2f_register_data');
253 console.log("Register callback: ", data);
254 if (deviceResponse.errorCode && deviceResponse.errorCode != 0) {
255 var u2f_return_code = document.getElementById('u2f_return_code');
256 u2f_return_code.style.display = u2f_return_code.style.display === 'none' ? '' : null;
257 if (deviceResponse.errorCode == "4") {
258 deviceResponse.errorCode = "4 - The presented device is not eligible for this request. For a registration request this may mean that the token is already registered, and for a sign request it may mean that the token does not know the presented key handle";
259 }
260 else if (deviceResponse.errorCode == "5") {
261 deviceResponse.errorCode = "5 - Timeout reached before request could be satisfied.";
262 }
263 u2f_return_code.innerHTML = lang_tfa.error_code + ': ' + deviceResponse.errorCode + ' ' + lang_tfa.reload_retry;
264 return;
265 }
266 reg.value = JSON.stringify(deviceResponse);
267 form.submit();
268 });
269 }, 1000);
270 }
271 });
272 });
273 }
274 if ($(this).val() == "none") {
275 $('#DisableTFAModal').modal('show');
276 $("option:selected").prop("selected", false);
277 }
278 });
279
280 // Reload after session timeout
281 var session_lifetime = <?=((int)$SESSION_LIFETIME * 1000) + 15000;?>;
282 <?php
283 if (isset($_SESSION['mailcow_cc_username'])):
284 ?>
285 setTimeout(function() {
286 location.reload();
287 }, session_lifetime);
288 <?php
289 endif;
290 ?>
291
292 // CSRF
293 $('<input type="hidden" value="<?= $_SESSION['CSRF']['TOKEN']; ?>">').attr('name', 'csrf_token').appendTo('form');
294 if (sessionStorage.scrollTop != "undefined") {
295 $(window).scrollTop(sessionStorage.scrollTop);
296 }
297});
298</script>
299
300 <div class="container footer">
301 <?php if (!empty($UI_TEXTS['ui_footer'])) { ?>
302 <hr><span class="rot-enc"><?=str_rot13($UI_TEXTS['ui_footer']);?></span>
303 <?php } ?>
304 </div>
305</body>
306</html>
307<?php
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200308if (isset($_SESSION['mailcow_cc_api'])) {
309 session_regenerate_id(true);
310 session_unset();
311 session_destroy();
312 session_write_close();
313 header("Location: /");
314}
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +0100315$stmt = null;
316$pdo = null;