Skip to content

Commit d2c8e2f

Browse files
committed
feat: gestione login tramite OTP/Token
1 parent b3ee55c commit d2c8e2f

29 files changed

Lines changed: 3205 additions & 31 deletions

File tree

actions.php

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use Modules\Emails\Template;
3030
use Notifications\EmailNotification;
3131
use Util\Zip;
32+
use Permissions;
3233

3334
if (empty($structure) || empty($structure['enabled'])) {
3435
exit(tr('Accesso negato'));
@@ -41,7 +42,28 @@
4142
// Upload allegati e rimozione
4243
if (filter('op') == 'aggiungi-allegato' || filter('op') == 'rimuovi-allegato') {
4344
// Controllo sui permessi di scrittura per il modulo
44-
if (Modules::getPermission($id_module) != 'rw') {
45+
$has_write_permission = false;
46+
47+
// Verifica permessi in base al tipo di accesso
48+
if (Permissions::isTokenAccess()) {
49+
// Per accesso tramite token, verifica i permessi del token
50+
$token_info = $_SESSION['token_access'];
51+
$token_permission = $token_info['permessi'] ?? 'r';
52+
53+
// Per gli allegati, verifica i permessi specifici del token
54+
if (filter('op') == 'aggiungi-allegato') {
55+
// Caricamento allegati: permessi 'ra', 'rwa' o 'rw'
56+
$has_write_permission = in_array($token_permission, ['ra', 'rwa', 'rw']);
57+
} elseif (filter('op') == 'rimuovi-allegato') {
58+
// Rimozione allegati: solo permessi 'rwa' o 'rw'
59+
$has_write_permission = in_array($token_permission, ['rwa', 'rw']);
60+
}
61+
} else {
62+
// Per accesso normale, usa i permessi standard del modulo
63+
$has_write_permission = (Modules::getPermission($id_module) == 'rw');
64+
}
65+
66+
if (!$has_write_permission) {
4567
flash()->error(tr('Non hai permessi di scrittura per il modulo _MODULE_', [
4668
'_MODULE_' => '"'.Module::find($id_module)->getTranslation('title').'"',
4769
]));
@@ -154,7 +176,14 @@
154176
}
155177
}
156178

157-
redirect(base_path().'/editor.php?id_module='.$id_module.'&id_record='.$id_record.((!empty($options['id_plugin'])) ? '#tab_'.$options['id_plugin'] : ''));
179+
// Determina il redirect appropriato in base al tipo di accesso
180+
if (Permissions::isTokenAccess() && !empty($_SESSION['token_access']['id_module_target']) && !empty($_SESSION['token_access']['id_record_target'])) {
181+
// Per accesso tramite token, redirect a shared_editor.php
182+
redirect(base_path().'/shared_editor.php?id_module='.$id_module.'&id_record='.$id_record.((!empty($options['id_plugin'])) ? '#tab_'.$options['id_plugin'] : ''));
183+
} else {
184+
// Per accesso normale, redirect a editor.php
185+
redirect(base_path().'/editor.php?id_module='.$id_module.'&id_record='.$id_record.((!empty($options['id_plugin'])) ? '#tab_'.$options['id_plugin'] : ''));
186+
}
158187
}
159188
}
160189

ajax.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@
7070

7171
case 'list_attachments':
7272
$category = get('id_category') ? Categoria::find(get('id_category'))->name : null;
73-
echo '{( "name": "filelist_and_upload", "id_module": "'.$id_module.'", "id_record": "'.$id_record.'", "id_plugin": "'.$id_plugin.'", "category": "'.$category.'" )}';
73+
$upload_only = get('upload_only') === 'true' ? 'true' : 'false';
74+
$disable_edit = get('disable_edit') === 'true' ? 'true' : 'false';
75+
echo '{( "name": "filelist_and_upload", "id_module": "'.$id_module.'", "id_record": "'.$id_record.'", "id_plugin": "'.$id_plugin.'", "category": "'.$category.'", "upload_only": "'.$upload_only.'", "disable_edit": "'.$disable_edit.'" )}';
7476

7577
break;
7678

assets/src/js/functions/allegati.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ function ricaricaAllegati(gestione) {
143143
id_module: gestione.data('id_module'),
144144
id_plugin: gestione.data('id_plugin'),
145145
id_record: gestione.data('id_record'),
146-
id_category: gestione.data('id_category')
146+
id_category: gestione.data('id_category'),
147+
upload_only: gestione.data('upload_only') === true || gestione.data('upload_only') === 'true' ? 'true' : 'false',
148+
disable_edit: gestione.data('disable_edit') === true || gestione.data('disable_edit') === 'true' ? 'true' : 'false'
147149
}).toString();
148150

149151
$(id).load(globals.rootdir + "/ajax.php?" + params, function () {

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"digitick/sepa-xml": "^2.1",
3535
"doctrine/sql-formatter": "^1.5",
3636
"dragonmantank/cron-expression": "^1.0",
37+
"endroid/qr-code": "^6.0",
3738
"ezyang/htmlpurifier": "^4.8",
3839
"filp/whoops": "^2.15.0",
3940
"greenlion/php-sql-parser": "^4.5",

index.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use Illuminate\Database\QueryException;
2626

2727
$op = filter('op');
28+
$token = filter('token');
2829

2930
$microsoft = null;
3031

@@ -77,6 +78,13 @@
7778
}
7879

7980
if (Auth::check() && isset($dbo) && $dbo->isConnected() && $dbo->isInstalled()) {
81+
if (Permissions::isTokenAccess()) {
82+
if (!empty($_SESSION['token_access']['id_module_target']) && !empty($_SESSION['token_access']['id_record_target'])) {
83+
redirect(base_path().'/shared_editor.php?id_module='.$_SESSION['token_access']['id_module_target'].'&id_record='.$_SESSION['token_access']['id_record_target']);
84+
exit;
85+
}
86+
}
87+
8088
$module = Auth::firstModule();
8189

8290
if (!empty($module)) {
@@ -87,6 +95,12 @@
8795
exit;
8896
}
8997

98+
// Gestione accesso tramite token OTP
99+
if (!empty($token) && $dbo->isConnected() && $dbo->isInstalled()) {
100+
redirect(base_path().'/token_login.php?token='.urlencode($token));
101+
exit;
102+
}
103+
90104
// Modalità manutenzione
91105
if (!empty($config['maintenance_ip'])) {
92106
include_once base_dir().'/include/init/maintenance.php';

lib/util.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,3 +597,42 @@ function adjustBrightness($hexCode, $adjustPercent)
597597
return '#'.implode('', $hexCode);
598598
}
599599
}
600+
601+
if (!function_exists('blurEmail')) {
602+
/**
603+
* Funzione per offuscare parzialmente un indirizzo email
604+
*
605+
* @param string $email Indirizzo email da offuscare
606+
* @return string Email offuscata con asterischi
607+
*/
608+
function blurEmail($email) {
609+
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
610+
return $email;
611+
}
612+
613+
$parts = explode('@', $email);
614+
$username = $parts[0];
615+
$domain = $parts[1];
616+
617+
$username_length = strlen($username);
618+
619+
if ($username_length <= 2) {
620+
// Se il nome utente è molto corto, mostra solo il primo carattere
621+
$blurred_username = substr($username, 0, 1) . str_repeat('*', max(1, $username_length - 1));
622+
} elseif ($username_length <= 4) {
623+
// Per nomi utente corti, mostra primi 2 caratteri
624+
$blurred_username = substr($username, 0, 2) . str_repeat('*', $username_length - 2);
625+
} else {
626+
// Per nomi utente lunghi, mostra primi 2 e ultimi 1 carattere
627+
$visible_start = 2;
628+
$visible_end = 1;
629+
$stars_count = max(3, $username_length - $visible_start - $visible_end);
630+
631+
$blurred_username = substr($username, 0, $visible_start) .
632+
str_repeat('*', $stars_count) .
633+
substr($username, -$visible_end);
634+
}
635+
636+
return $blurred_username . '@' . $domain;
637+
}
638+
}

modules/emails/src/Mail.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ public static function build(?User $user = null, $template = null, $id_record =
3939
{
4040
$model = new static();
4141

42-
$model->created_by = $user->id;
42+
$model->created_by = (!empty($user) ? $user->id : null);
4343

4444
if (!empty($template)) {
4545
$model->id_template = $template->id;
4646
$model->id_account = $template->account->id;
4747
}
4848

49-
$model->id_record = $id_record;
49+
$model->id_record = (!empty($id_record) ? $id_record : null);
5050

5151
$model->save();
5252

modules/otp_tokens/actions.php

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
/*
4+
* OpenSTAManager: il software gestionale open source per l'assistenza tecnica e la fatturazione
5+
* Copyright (C) DevCode s.r.l.
6+
*
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
include_once __DIR__.'/../../core.php';
22+
23+
switch (post('op')) {
24+
case 'update':
25+
$id_utente = post('id_utente');
26+
$descrizione = post('descrizione');
27+
$tipo_accesso = post('tipo_accesso');
28+
$valido_dal = post('valido_dal') ?: null;
29+
$valido_al = post('valido_al') ?: null;
30+
$id_module_target = post('id_module_target') ?: 0;
31+
$id_record_target = post('id_record_target') ?: 0;
32+
$permessi = post('permessi') ?: null;
33+
$email = post('email');
34+
35+
// Validazione email per token OTP
36+
if ($tipo_accesso == 'otp' && empty($email)) {
37+
flash()->error(tr('L\'email è obbligatoria per i token con OTP'));
38+
break;
39+
}
40+
41+
if ($tipo_accesso == 'otp' && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
42+
flash()->error(tr('Inserire un indirizzo email valido'));
43+
break;
44+
}
45+
46+
// Aggiornamento record
47+
$dbo->update('zz_otp_tokens', [
48+
'id_utente' => $id_utente,
49+
'descrizione' => $descrizione,
50+
'tipo_accesso' => $tipo_accesso,
51+
'valido_dal' => $valido_dal,
52+
'valido_al' => $valido_al,
53+
'id_module_target' => $id_module_target,
54+
'id_record_target' => $id_record_target,
55+
'permessi' => $permessi,
56+
'email' => $email,
57+
], ['id' => $id_record]);
58+
59+
flash()->info(tr('Token aggiornato correttamente'));
60+
break;
61+
62+
case 'add':
63+
$descrizione = post('descrizione');
64+
// Generazione token sicuro
65+
$token = secure_random_string(32);
66+
67+
// Inserimento nuovo record
68+
$dbo->insert('zz_otp_tokens', [
69+
'token' => $token,
70+
'descrizione' => $descrizione,
71+
'enabled' => 0,
72+
]);
73+
74+
$id_record = $dbo->lastInsertedID();
75+
76+
if (isAjaxRequest()) {
77+
echo json_encode(['id' => $id_record, 'text' => $descrizione]);
78+
}
79+
80+
flash()->info(tr('Token creato correttamente'));
81+
break;
82+
83+
case 'delete':
84+
$dbo->query('DELETE FROM zz_otp_tokens WHERE id='.prepare($id_record));
85+
flash()->info(tr('Token eliminato!'));
86+
break;
87+
88+
case 'enable':
89+
$dbo->update('zz_otp_tokens', [
90+
'enabled' => 1,
91+
], ['id' => $id_record]);
92+
93+
flash()->info(tr('Token abilitato!'));
94+
break;
95+
96+
case 'disable':
97+
$dbo->update('zz_otp_tokens', [
98+
'enabled' => 0,
99+
], ['id' => $id_record]);
100+
101+
flash()->info(tr('Token disabilitato!'));
102+
break;
103+
104+
case 'delete':
105+
$dbo->query('DELETE FROM zz_otp_tokens WHERE id='.prepare($id_record));
106+
107+
flash()->info(tr('Token eliminato!'));
108+
break;
109+
}

modules/otp_tokens/add.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
/*
3+
* OpenSTAManager: il software gestionale open source per l'assistenza tecnica e la fatturazione
4+
* Copyright (C) DevCode s.r.l.
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
*/
19+
20+
include_once __DIR__.'/../../core.php';
21+
22+
?>
23+
24+
<form action="" method="post" id="add-form">
25+
<fieldset>
26+
<input type="hidden" name="op" value="add">
27+
<input type="hidden" name="backto" value="record-edit">
28+
29+
<div class="card card-primary">
30+
<div class="card-header">
31+
<h3 class="card-title"><?php echo tr('Nuovo Token'); ?></h3>
32+
</div>
33+
34+
<div class="card-body">
35+
36+
<div class="row">
37+
<div class="col-md-12">
38+
{[ "type": "text", "label": "<?php echo tr('Descrizione'); ?>", "name": "descrizione", "required": 1, "placeholder": "<?php echo tr('Inserisci una descrizione per il token'); ?>" ]}
39+
</div>
40+
</div>
41+
</div>
42+
</div>
43+
44+
<!-- PULSANTI -->
45+
<div class="row">
46+
<div class="col-md-12 text-right">
47+
<button type="submit" class="btn btn-primary">
48+
<i class="fa fa-plus"></i> <?php echo tr('Aggiungi'); ?>
49+
</button>
50+
</div>
51+
</div>
52+
</fieldset>
53+
</form>

0 commit comments

Comments
 (0)