mirror of
https://github.com/Bubka/2FAuth.git
synced 2024-11-25 17:54:57 +01:00
Add a test email feature to the admin panel - Closes #307
This commit is contained in:
parent
04078b09aa
commit
88d37394d3
@ -3,10 +3,12 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Facades\Settings;
|
use App\Facades\Settings;
|
||||||
|
use App\Notifications\TestEmailSettingNotification;
|
||||||
use App\Services\ReleaseRadarService;
|
use App\Services\ReleaseRadarService;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class SystemController extends Controller
|
class SystemController extends Controller
|
||||||
{
|
{
|
||||||
@ -58,4 +60,20 @@ public function latestRelease(Request $request, ReleaseRadarService $releaseRada
|
|||||||
|
|
||||||
return response()->json(['newRelease' => $release]);
|
return response()->json(['newRelease' => $release]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a test email.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function testEmail(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$request->user()->notify(new TestEmailSettingNotification());
|
||||||
|
} catch (\Throwable $th) {
|
||||||
|
Log::error($th->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Ok']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
64
app/Notifications/TestEmailSettingNotification.php
Normal file
64
app/Notifications/TestEmailSettingNotification.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
use Illuminate\Support\Facades\Lang;
|
||||||
|
|
||||||
|
class TestEmailSettingNotification extends Notification
|
||||||
|
{
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * The callback that should be used to create the reset password URL.
|
||||||
|
// *
|
||||||
|
// * @var \Closure|null
|
||||||
|
// */
|
||||||
|
// protected static ?Closure $createUrlCallback;
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * The callback that should be used to build the mail message.
|
||||||
|
// *
|
||||||
|
// * @var \Closure|null
|
||||||
|
// */
|
||||||
|
// protected static ?Closure $toMailCallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TestEmailSettingNotification constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the notification's delivery channels.
|
||||||
|
*
|
||||||
|
* @param mixed $notifiable
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function via($notifiable)
|
||||||
|
{
|
||||||
|
return ['mail'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the mail representation of the notification.
|
||||||
|
*
|
||||||
|
* @param mixed $notifiable
|
||||||
|
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||||
|
*/
|
||||||
|
public function toMail($notifiable)
|
||||||
|
{
|
||||||
|
return (new MailMessage)
|
||||||
|
->subject(Lang::get('notifications.test_email_settings.subject'))
|
||||||
|
->greeting(Lang::get('notifications.hello'))
|
||||||
|
->line(
|
||||||
|
Lang::get('notifications.test_email_settings.reason')
|
||||||
|
)
|
||||||
|
->line(
|
||||||
|
Lang::get('notifications.test_email_settings.success')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
4
resources/js/icons.js
vendored
4
resources/js/icons.js
vendored
@ -49,7 +49,8 @@ import {
|
|||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
faStar
|
faStar,
|
||||||
|
faPaperPlane,
|
||||||
} from '@fortawesome/free-regular-svg-icons'
|
} from '@fortawesome/free-regular-svg-icons'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -105,6 +106,7 @@ library.add(
|
|||||||
faStar,
|
faStar,
|
||||||
faChevronRight,
|
faChevronRight,
|
||||||
faOpenid,
|
faOpenid,
|
||||||
|
faPaperPlane,
|
||||||
);
|
);
|
||||||
|
|
||||||
export default FontAwesomeIcon
|
export default FontAwesomeIcon
|
8
resources/js/services/systemService.js
vendored
8
resources/js/services/systemService.js
vendored
@ -17,6 +17,14 @@ export default {
|
|||||||
*/
|
*/
|
||||||
getLastRelease(config = {}) {
|
getLastRelease(config = {}) {
|
||||||
return webClient.get('latestRelease', { ...config })
|
return webClient.get('latestRelease', { ...config })
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns Promise
|
||||||
|
*/
|
||||||
|
sendTestEmail(config = {}) {
|
||||||
|
return webClient.post('testEmail', { ...config })
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -4,16 +4,19 @@
|
|||||||
import systemService from '@/services/systemService'
|
import systemService from '@/services/systemService'
|
||||||
import { useAppSettingsStore } from '@/stores/appSettings'
|
import { useAppSettingsStore } from '@/stores/appSettings'
|
||||||
import { useNotifyStore } from '@/stores/notify'
|
import { useNotifyStore } from '@/stores/notify'
|
||||||
|
import { useUserStore } from '@/stores/user'
|
||||||
import VersionChecker from '@/components/VersionChecker.vue'
|
import VersionChecker from '@/components/VersionChecker.vue'
|
||||||
import CopyButton from '@/components/CopyButton.vue'
|
import CopyButton from '@/components/CopyButton.vue'
|
||||||
|
|
||||||
const $2fauth = inject('2fauth')
|
const $2fauth = inject('2fauth')
|
||||||
|
const user = useUserStore()
|
||||||
const notify = useNotifyStore()
|
const notify = useNotifyStore()
|
||||||
const appSettings = useAppSettingsStore()
|
const appSettings = useAppSettingsStore()
|
||||||
const returnTo = useStorage($2fauth.prefix + 'returnTo', 'accounts')
|
const returnTo = useStorage($2fauth.prefix + 'returnTo', 'accounts')
|
||||||
|
|
||||||
const infos = ref()
|
const infos = ref()
|
||||||
const listInfos = ref(null)
|
const listInfos = ref(null)
|
||||||
|
const isSendingTestEmail = ref(false)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves a setting on the backend
|
* Saves a setting on the backend
|
||||||
@ -26,6 +29,18 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a test email
|
||||||
|
*/
|
||||||
|
function sendTestEmail() {
|
||||||
|
isSendingTestEmail.value = true;
|
||||||
|
|
||||||
|
systemService.sendTestEmail()
|
||||||
|
.finally(() => {
|
||||||
|
isSendingTestEmail.value = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
onBeforeRouteLeave((to) => {
|
onBeforeRouteLeave((to) => {
|
||||||
if (! to.name.startsWith('admin.')) {
|
if (! to.name.startsWith('admin.')) {
|
||||||
notify.clear()
|
notify.clear()
|
||||||
@ -53,6 +68,22 @@
|
|||||||
<!-- Check for update -->
|
<!-- Check for update -->
|
||||||
<FormCheckbox v-model="appSettings.checkForUpdate" @update:model-value="val => saveSetting('checkForUpdate', val)" fieldName="checkForUpdate" label="commons.check_for_update" help="commons.check_for_update_help" />
|
<FormCheckbox v-model="appSettings.checkForUpdate" @update:model-value="val => saveSetting('checkForUpdate', val)" fieldName="checkForUpdate" label="commons.check_for_update" help="commons.check_for_update_help" />
|
||||||
<VersionChecker />
|
<VersionChecker />
|
||||||
|
<div class="field">
|
||||||
|
<!-- <h5 class="title is-5">{{ $t('settings.security') }}</h5> -->
|
||||||
|
<label class="label" v-html="$t('admin.forms.test_email.label')" />
|
||||||
|
<p class="help" v-html="$t('admin.forms.test_email.help')" />
|
||||||
|
<p class="help" v-html="$t('admin.forms.test_email.email_will_be_send_to_x', { email: user.email })" />
|
||||||
|
</div>
|
||||||
|
<div class="columns is-mobile is-vcentered">
|
||||||
|
<div class="column is-narrow">
|
||||||
|
<button type="button" :class="isSendingTestEmail ? 'is-loading' : ''" class="button is-link is-rounded is-small" @click="sendTestEmail">
|
||||||
|
<span class="icon is-small">
|
||||||
|
<FontAwesomeIcon :icon="['far', 'paper-plane']" />
|
||||||
|
</span>
|
||||||
|
<span>{{ $t('commons.send') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<h4 class="title is-4 pt-4 has-text-grey-light">{{ $t('settings.security') }}</h4>
|
<h4 class="title is-4 pt-4 has-text-grey-light">{{ $t('settings.security') }}</h4>
|
||||||
<!-- protect db -->
|
<!-- protect db -->
|
||||||
<FormCheckbox v-model="appSettings.useEncryption" @update:model-value="val => saveSetting('useEncryption', val)" fieldName="useEncryption" label="admin.forms.use_encryption.label" help="admin.forms.use_encryption.help" />
|
<FormCheckbox v-model="appSettings.useEncryption" @update:model-value="val => saveSetting('useEncryption', val)" fieldName="useEncryption" label="admin.forms.use_encryption.label" help="admin.forms.use_encryption.help" />
|
||||||
|
@ -79,6 +79,11 @@
|
|||||||
'is_admin' => [
|
'is_admin' => [
|
||||||
'label' => 'Is administrator',
|
'label' => 'Is administrator',
|
||||||
'help' => 'Give administrator rights to the user. Administrators have permissions to manage the whole app, i.e. settings and other users, but cannot generate password for a 2FA they don\'t own.'
|
'help' => 'Give administrator rights to the user. Administrators have permissions to manage the whole app, i.e. settings and other users, but cannot generate password for a 2FA they don\'t own.'
|
||||||
|
],
|
||||||
|
'test_email' => [
|
||||||
|
'label' => 'Email configuration test',
|
||||||
|
'help' => 'Send a test email to control your instance\'s email configuration. It is important to have a working configuration, otherwise users will not be able to request a reset password.',
|
||||||
|
'email_will_be_send_to_x' => 'The email will be send to <span class="is-family-code has-text-info">:email</span>',
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -79,5 +79,6 @@
|
|||||||
'nothing' => 'nothing',
|
'nothing' => 'nothing',
|
||||||
'no_result' => 'No result',
|
'no_result' => 'No result',
|
||||||
'information' => 'Information',
|
'information' => 'Information',
|
||||||
'permissions' => 'Permissions'
|
'permissions' => 'Permissions',
|
||||||
|
'send' => 'Send',
|
||||||
];
|
];
|
||||||
|
23
resources/lang/en/notifications.php
Normal file
23
resources/lang/en/notifications.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Notifications Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines are used during authentication for various
|
||||||
|
| messages that we need to display to the user. You are free to modify
|
||||||
|
| these language lines according to your application's requirements.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'hello' => 'Hello',
|
||||||
|
'test_email_settings' => [
|
||||||
|
'subject' => '2FAuth test email',
|
||||||
|
'reason' => 'You are receiving this email because you requested a test email to validate the email settings of your 2FAuth instance.',
|
||||||
|
'success' => 'Good news, it works :)'
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
@ -83,6 +83,7 @@
|
|||||||
*/
|
*/
|
||||||
Route::group(['middleware' => ['behind-auth', 'admin']], function () {
|
Route::group(['middleware' => ['behind-auth', 'admin']], function () {
|
||||||
Route::get('infos', [SystemController::class, 'infos'])->name('system.infos');
|
Route::get('infos', [SystemController::class, 'infos'])->name('system.infos');
|
||||||
|
Route::post('testEmail', [SystemController::class, 'testEmail'])->name('system.testEmail');
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::get('latestRelease', [SystemController::class, 'latestRelease'])->name('system.latestRelease');
|
Route::get('latestRelease', [SystemController::class, 'latestRelease'])->name('system.latestRelease');
|
||||||
|
@ -4,8 +4,9 @@
|
|||||||
|
|
||||||
use App\Http\Controllers\SystemController;
|
use App\Http\Controllers\SystemController;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Notifications\TestEmailSettingNotification;
|
||||||
use App\Services\ReleaseRadarService;
|
use App\Services\ReleaseRadarService;
|
||||||
use Illuminate\Foundation\Testing\WithoutMiddleware;
|
use Illuminate\Support\Facades\Notification;
|
||||||
use PHPUnit\Framework\Attributes\CoversClass;
|
use PHPUnit\Framework\Attributes\CoversClass;
|
||||||
use Tests\FeatureTestCase;
|
use Tests\FeatureTestCase;
|
||||||
|
|
||||||
@ -15,8 +16,6 @@
|
|||||||
#[CoversClass(SystemController::class)]
|
#[CoversClass(SystemController::class)]
|
||||||
class SystemControllerTest extends FeatureTestCase
|
class SystemControllerTest extends FeatureTestCase
|
||||||
{
|
{
|
||||||
//use WithoutMiddleware;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \App\Models\User|\Illuminate\Contracts\Auth\Authenticatable
|
* @var \App\Models\User|\Illuminate\Contracts\Auth\Authenticatable
|
||||||
*/
|
*/
|
||||||
@ -116,4 +115,38 @@ public function test_latestrelease_runs_manual_scan()
|
|||||||
'newRelease' => 'new_release',
|
'newRelease' => 'new_release',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function test_testEmail_sends_a_notification()
|
||||||
|
{
|
||||||
|
Notification::fake();
|
||||||
|
|
||||||
|
$response = $this->actingAs($this->admin, 'web-guard')
|
||||||
|
->json('POST', '/testEmail', []);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
Notification::assertSentTo($this->admin, TestEmailSettingNotification::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function test_testEmail_returns_unauthorized()
|
||||||
|
{
|
||||||
|
$response = $this->json('GET', '/infos')
|
||||||
|
->assertUnauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function test_testEmail_returns_forbidden()
|
||||||
|
{
|
||||||
|
$response = $this->actingAs($this->user, 'api-guard')
|
||||||
|
->json('GET', '/infos')
|
||||||
|
->assertForbidden();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user