Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose the 'sanitize' system for backup restores to the web GUI #15296

Merged
merged 3 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ REQUIRE_SAML=false
API_THROTTLE_PER_MINUTE=120
CSV_ESCAPE_FORMULAS=true
LIVEWIRE_URL_PREFIX=null
SANITIZE_BY_DEFAULT=false
uberbrady marked this conversation as resolved.
Show resolved Hide resolved

# --------------------------------------------
# OPTIONAL: HASHING
Expand Down
28 changes: 22 additions & 6 deletions app/Http/Controllers/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -1203,7 +1203,7 @@ public function postUploadBackup(Request $request) : RedirectResponse
* @author [A. Gianotto] [<[email protected]>]
* @since [v6.0]
*/
public function postRestore($filename = null) : RedirectResponse
public function postRestore(Request $request, $filename = null): RedirectResponse
{

if (! config('app.lock_passwords')) {
Expand All @@ -1223,13 +1223,29 @@ public function postRestore($filename = null) : RedirectResponse

Log::debug('Attempting to restore from: '. storage_path($path).'/'.$filename);

// run the restore command
Artisan::call('snipeit:restore',
[
$restore_params = [
'--force' => true,
'--no-progress' => true,
'filename' => storage_path($path).'/'.$filename
]);
'filename' => storage_path($path) . '/' . $filename
];

if ($request->input('clean')) {
Log::debug("Attempting 'clean' - first, guessing prefix...");
Artisan::call('snipeit:restore', [
'--sanitize-guess-prefix' => true,
'filename' => storage_path($path) . '/' . $filename
]);
$guess_prefix_output = Artisan::output();
Log::debug("Sanitize output is: $guess_prefix_output");
list($prefix, $_output) = explode("\n", $guess_prefix_output);
Log::debug("prefix is: '$prefix'");
$restore_params['--sanitize-with-prefix'] = $prefix;
}

// run the restore command
Artisan::call('snipeit:restore',
$restore_params
);

// If it's greater than 300, it probably worked
$output = Artisan::output();
Expand Down
2 changes: 2 additions & 0 deletions config/backup.php
Original file line number Diff line number Diff line change
Expand Up @@ -237,4 +237,6 @@
],
],

'sanitize_by_default' => env('SANITIZE_BY_DEFAULT', false),

];
103 changes: 32 additions & 71 deletions resources/assets/js/snipeit.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,84 +82,45 @@ pieOptions = {

var baseUrl = $('meta[name="baseUrl"]').attr('content');

(function($, settings) {
var Components = {};
Components.modals = {};
$(function () {

// confirm restore modal
Components.modals.confirmRestore = function() {
var $el = $('table');

var events = {
'click': function(evnt) {
var $context = $(this);
var $restoreConfirmModal = $('#restoreConfirmModal');
var href = $context.attr('href');
var message = $context.attr('data-content');
var title = $context.attr('data-title');

$('#restoreConfirmModalLabel').text(title);
$restoreConfirmModal.find('.modal-body').text(message);
$('#restoreForm').attr('action', href);
$restoreConfirmModal.modal({
show: true
});
return false;
}
};
var $el = $('table');

var render = function() {
$el.on('click', '.restore-asset', events['click']);
};
// confirm restore modal

return {
render: render
};
};
$el.on('click', '.restore-asset', function (evnt) {
var $context = $(this);
var $restoreConfirmModal = $('#restoreConfirmModal');
var href = $context.attr('href');
var message = $context.attr('data-content');
var title = $context.attr('data-title');

$('#confirmModalLabel').text(title);
$restoreConfirmModal.find('.modal-body').text(message);
$('#restoreForm').attr('action', href);
$restoreConfirmModal.modal({
show: true
});
return false;
});

// confirm delete modal
Components.modals.confirmDelete = function() {
var $el = $('table');

var events = {
'click': function(evnt) {
var $context = $(this);
var $dataConfirmModal = $('#dataConfirmModal');
var href = $context.attr('href');
var message = $context.attr('data-content');
var title = $context.attr('data-title');

$('#myModalLabel').text(title);
$dataConfirmModal.find('.modal-body').text(message);
$('#deleteForm').attr('action', href);
$dataConfirmModal.modal({
show: true
});
return false;
}
};

var render = function() {
$el.on('click', '.delete-asset', events['click']);
};

return {
render: render
};
};


/**
* Application start point
* Component definition stays out of load event, execution only happens.
*/
$(function() {
new Components.modals.confirmRestore().render();
new Components.modals.confirmDelete().render();
$el.on('click', '.delete-asset', function (evnt) {
var $context = $(this);
var $dataConfirmModal = $('#dataConfirmModal');
var href = $context.attr('href');
var message = $context.attr('data-content');
var title = $context.attr('data-title');

$('#myModalLabel').text(title);
$dataConfirmModal.find('.modal-body').text(message);
$('#deleteForm').attr('action', href);
$dataConfirmModal.modal({
show: true
});
return false;
});
}(jQuery, window.snipeit.settings));

$(document).ready(function () {

/*
* Slideout help menu
Expand Down
2 changes: 2 additions & 0 deletions resources/lang/en-US/admin/settings/general.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
'backups' => 'Backups',
'backups_help' => 'Create, download, and restore backups ',
'backups_restoring' => 'Restoring from Backup',
'backups_clean' => 'Clean the backed-up database before restore',
'backups_clean_helptext' => "This can be useful if you're changing between database versions",
'backups_upload' => 'Upload Backup',
'backups_path' => 'Backups on the server are stored in <code>:path</code>',
'backups_restore_warning' => 'Use the restore button <small><span class="btn btn-xs btn-warning"><i class="text-white fas fa-retweet" aria-hidden="true"></i></span></small> to restore from a previous backup. (This does not currently work with S3 file storage or Docker.)<br><br>Your <strong>entire :app_name database and any uploaded files will be completely replaced</strong> by what\'s in the backup file. ',
Expand Down
63 changes: 51 additions & 12 deletions resources/views/settings/backups.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
@stop

@section('header_right')
<a href="{{ route('settings.index') }}" class="btn btn-default pull-right" style="margin-left: 5px;">
<a href="{{ route('settings.index') }}" class="btn btn-default pull-right" style="margin-left: 5px;"
xmlns="http://www.w3.org/1999/html">
uberbrady marked this conversation as resolved.
Show resolved Hide resolved
{{ trans('general.back') }}
</a>

Expand All @@ -21,8 +22,36 @@
{{-- Page content --}}
@section('content')

<div class="modal modal-warning fade" tabindex="-1" role="dialog" id="backupRestoreModal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form method="post" role="form">
<div class="modal-header">
<h4 class="modal-title">Modal title</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>{{ trans('admin/settings/message.backup.restore_warning') }}</p>
<p><label><input type="checkbox"
name="clean" {{ config('backup.sanitize_by_default') ? "checked='checked'" : "" }}>{{ trans('admin/settings/general.backups_clean') }}
</label></p>
<p>{{ trans('admin/settings/general.backups_clean_helptext') }}</p>
</div>
<div class="modal-footer">
{{ csrf_field() }}
{{ method_field('POST') }}
<button type="button" class="btn btn-default pull-left"
data-dismiss="modal">{{ trans('general.cancel') }}</button>
<button type="submit" class="btn btn-outline">{{ trans('general.yes') }}</button>
</div>
</form>
</div>
</div>
</div>

<div class="row">
<div class="row">

<div class="col-md-8">

Expand Down Expand Up @@ -85,13 +114,13 @@ class="btn delete-asset btn-danger btn-sm disabled">
</a>
@endif

<a data-html="true"
href="{{ route('settings.backups.restore', $file['filename']) }}"
class="btn btn-warning btn-sm restore-asset {{ (config('app.lock_passwords')) ? ' disabled': '' }}"
data-toggle="modal"
data-content="{{ trans('admin/settings/message.backup.restore_warning') }}"
data-title="{{ trans('admin/settings/message.backup.restore_confirm', array('filename' => e($file['filename']))) }}"
onClick="return false;">
<a data-html="true"
href="{{ route('settings.backups.restore', $file['filename']) }}"
class="btn btn-warning btn-sm restore-backup {{ (config('app.lock_passwords')) ? ' disabled': '' }}"
{{-- data-toggle="modal"--}}
data-target="#backupRestoreModal"
data-title="{{ trans('admin/settings/message.backup.restore_confirm', array('filename' => e($file['filename']))) }}"
onClick="return false;">
<i class="fas fa-retweet" aria-hidden="true"></i>
<span class="sr-only">{{ trans('general.restore') }}</span>
</a>
Expand Down Expand Up @@ -226,11 +255,21 @@ class="btn btn-warning btn-sm restore-asset {{ (config('app.lock_passwords')) ?
});
}




});
});

// due to dynamic loading, we have to use the below 'weird' way of adding event handlers instead of just saying
// $('.restore-backup').on( .....
$('table').on('click', '.restore-backup', function (event) {
event.preventDefault();
var modal = $('#backupRestoreModal');
modal.find('.modal-title').text($(this).data('title'));
modal.find('form').attr('action', $(this).attr('href'));
modal.modal({
show: true
});
return false;
})
</script>
@stop

Loading