mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-22 02:36:32 +00:00
feat(GUI): enable "unsafe" mode (#578)
This setting makes Etcher not filter non-removable drives, allowing you to arbitrarily write to your system drives. This is a dangerous option, therefore we present it in a separate section of the settings page, and show an informative confirmation dialog. Change-Type: minor Changelog-Entry: Add an "unsafe" option to bypass drive protection. Fixes: https://github.com/resin-io/etcher/issues/480 Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
This commit is contained in:
parent
e696d0722d
commit
a3dc8624b1
@ -5945,6 +5945,9 @@ html {
|
|||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-bottom: 10px; }
|
margin-bottom: 10px; }
|
||||||
|
|
||||||
|
.space-top-large, .page-settings .subtitle {
|
||||||
|
margin-top: 30px; }
|
||||||
|
|
||||||
.space-top-medium {
|
.space-top-medium {
|
||||||
margin-top: 15px; }
|
margin-top: 15px; }
|
||||||
|
|
||||||
@ -5952,6 +5955,9 @@ html {
|
|||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
margin-bottom: 30px; }
|
margin-bottom: 30px; }
|
||||||
|
|
||||||
|
.space-bottom-medium, .page-settings .subtitle {
|
||||||
|
margin-bottom: 15px; }
|
||||||
|
|
||||||
.space-bottom-large {
|
.space-bottom-large {
|
||||||
margin-bottom: 30px; }
|
margin-bottom: 30px; }
|
||||||
|
|
||||||
@ -6515,6 +6521,36 @@ button.btn:focus, button.progress-button:focus {
|
|||||||
.page-settings .checkbox input[type="checkbox"]:not(:checked) + * {
|
.page-settings .checkbox input[type="checkbox"]:not(:checked) + * {
|
||||||
color: #ddd; }
|
color: #ddd; }
|
||||||
|
|
||||||
|
.modal-settings-dangerous-modal {
|
||||||
|
max-width: 50%; }
|
||||||
|
.modal-settings-dangerous-modal .modal-header {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #555555;
|
||||||
|
text-transform: initial;
|
||||||
|
font-size: 15px;
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 0 0 15px; }
|
||||||
|
.modal-settings-dangerous-modal .modal-title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center; }
|
||||||
|
.modal-settings-dangerous-modal .modal-title .glyphicon, .modal-settings-dangerous-modal .modal-title .tick {
|
||||||
|
margin-right: 3px;
|
||||||
|
color: #d9534f; }
|
||||||
|
.modal-settings-dangerous-modal .modal-content {
|
||||||
|
padding: 25px 30px;
|
||||||
|
height: 220px; }
|
||||||
|
.modal-settings-dangerous-modal .modal-body {
|
||||||
|
padding: 0; }
|
||||||
|
.modal-settings-dangerous-modal .modal-footer {
|
||||||
|
padding: 0;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #eeeeee;
|
||||||
|
display: flex; }
|
||||||
|
.modal-settings-dangerous-modal .modal-footer > .btn, .modal-settings-dangerous-modal .modal-footer > .progress-button {
|
||||||
|
flex-basis: 50%; }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2016 Resin.io
|
* Copyright 2016 Resin.io
|
||||||
*
|
*
|
||||||
|
@ -37,6 +37,7 @@ const DEFAULT_STATE = Immutable.fromJS({
|
|||||||
speed: 0
|
speed: 0
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
|
unsafeMode: false,
|
||||||
errorReporting: true,
|
errorReporting: true,
|
||||||
unmountOnSuccess: true,
|
unmountOnSuccess: true,
|
||||||
validateWriteOnSuccess: true,
|
validateWriteOnSuccess: true,
|
||||||
|
@ -27,9 +27,11 @@ const EventEmitter = require('events').EventEmitter;
|
|||||||
const drivelist = require('drivelist');
|
const drivelist = require('drivelist');
|
||||||
|
|
||||||
const MODULE_NAME = 'Etcher.Modules.DriveScanner';
|
const MODULE_NAME = 'Etcher.Modules.DriveScanner';
|
||||||
const driveScanner = angular.module(MODULE_NAME, []);
|
const driveScanner = angular.module(MODULE_NAME, [
|
||||||
|
require('../models/settings')
|
||||||
|
]);
|
||||||
|
|
||||||
driveScanner.factory('DriveScannerService', () => {
|
driveScanner.factory('DriveScannerService', (SettingsModel) => {
|
||||||
const DRIVE_SCANNER_INTERVAL_MS = 2000;
|
const DRIVE_SCANNER_INTERVAL_MS = 2000;
|
||||||
const emitter = new EventEmitter();
|
const emitter = new EventEmitter();
|
||||||
|
|
||||||
@ -38,6 +40,10 @@ driveScanner.factory('DriveScannerService', () => {
|
|||||||
return Rx.Observable.fromNodeCallback(drivelist.list)();
|
return Rx.Observable.fromNodeCallback(drivelist.list)();
|
||||||
})
|
})
|
||||||
.map((drives) => {
|
.map((drives) => {
|
||||||
|
if (SettingsModel.get('unsafeMode')) {
|
||||||
|
return drives;
|
||||||
|
}
|
||||||
|
|
||||||
return _.reject(drives, (drive) => {
|
return _.reject(drives, (drive) => {
|
||||||
return drive.system;
|
return drive.system;
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 Resin.io
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function($uibModalInstance, message) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Modal message
|
||||||
|
* @property
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
this.message = message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Reject the dangerous setting
|
||||||
|
* @function
|
||||||
|
* @public
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* SettingsDangerousModalController.reject();
|
||||||
|
*/
|
||||||
|
this.reject = () => {
|
||||||
|
$uibModalInstance.close(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Accept the dangerous setting
|
||||||
|
* @function
|
||||||
|
* @public
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* SettingsDangerousModalController.accept();
|
||||||
|
*/
|
||||||
|
this.accept = () => {
|
||||||
|
$uibModalInstance.close(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
@ -16,14 +16,26 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
module.exports = function(SettingsModel) {
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
module.exports = function($uibModal, SettingsModel) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Refresh current settings
|
||||||
|
* @function
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
this.refreshSettings = () => {
|
||||||
|
this.currentData = SettingsModel.getAll();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Current settings value
|
* @summary Current settings value
|
||||||
* @type Object
|
* @type Object
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
this.currentData = SettingsModel.getAll();
|
this.currentData = {};
|
||||||
|
this.refreshSettings();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Settings model
|
* @summary Settings model
|
||||||
@ -32,4 +44,36 @@ module.exports = function(SettingsModel) {
|
|||||||
*/
|
*/
|
||||||
this.model = SettingsModel;
|
this.model = SettingsModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Enable a dangerous setting
|
||||||
|
* @function
|
||||||
|
* @public
|
||||||
|
*
|
||||||
|
* @param {String} name - setting name
|
||||||
|
* @param {String} message - danger message
|
||||||
|
* @returns {Undefined}
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* SettingsController.enableDangerousSetting('unsafeMode', 'Don\'t do this!');
|
||||||
|
*/
|
||||||
|
this.enableDangerousSetting = (name, message) => {
|
||||||
|
if (!this.currentData[name]) {
|
||||||
|
this.model.set(name, false);
|
||||||
|
return this.refreshSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
$uibModal.open({
|
||||||
|
animation: true,
|
||||||
|
templateUrl: './pages/settings/templates/settings-dangerous-modal.tpl.html',
|
||||||
|
controller: 'SettingsDangerousModalController as modal',
|
||||||
|
size: 'settings-dangerous-modal',
|
||||||
|
resolve: {
|
||||||
|
message: _.constant(message)
|
||||||
|
}
|
||||||
|
}).result.then((userAccepted) => {
|
||||||
|
this.model.set(name, Boolean(userAccepted));
|
||||||
|
this.refreshSettings();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -28,6 +28,7 @@ const SettingsPage = angular.module(MODULE_NAME, [
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
SettingsPage.controller('SettingsController', require('./controllers/settings'));
|
SettingsPage.controller('SettingsController', require('./controllers/settings'));
|
||||||
|
SettingsPage.controller('SettingsDangerousModalController', require('./controllers/settings-dangerous-modal'));
|
||||||
|
|
||||||
SettingsPage.config(($stateProvider) => {
|
SettingsPage.config(($stateProvider) => {
|
||||||
$stateProvider
|
$stateProvider
|
||||||
|
@ -18,3 +18,52 @@
|
|||||||
color: $gray-light;
|
color: $gray-light;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-settings .subtitle {
|
||||||
|
@extend .space-top-large;
|
||||||
|
@extend .space-bottom-medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-settings-dangerous-modal {
|
||||||
|
max-width: 50%;
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
color: $gray;
|
||||||
|
text-transform: initial;
|
||||||
|
font-size: 15px;
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 0 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.glyphicon {
|
||||||
|
margin-right: 3px;
|
||||||
|
color: $btn-danger-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
padding: 25px 30px;
|
||||||
|
height: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
padding: 0;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid $gray-lighter;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
& > .btn {
|
||||||
|
flex-basis: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title">
|
||||||
|
<span class="glyphicon glyphicon-exclamation-sign"></span>
|
||||||
|
<span>Warning!</span>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Are you sure you want to turn this on? {{ modal.message }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-danger"
|
||||||
|
ng-click="modal.accept()">YES, CONTINUE</button>
|
||||||
|
|
||||||
|
<button class="btn btn-default"
|
||||||
|
ng-click="modal.reject()">CANCEL</button>
|
||||||
|
</div>
|
@ -30,4 +30,16 @@
|
|||||||
<span>Validate write on success</span>
|
<span>Validate write on success</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h3 class="subtitle">Advanced</h3>
|
||||||
|
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox"
|
||||||
|
ng-model="settings.currentData.unsafeMode"
|
||||||
|
ng-change="settings.enableDangerousSetting('unsafeMode', 'You will be able to burn to your system drives.')">
|
||||||
|
|
||||||
|
<span>Unsafe mode <span class="label label-danger">DANGEROUS</span></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -33,6 +33,10 @@ $spacing-tiny: 5px;
|
|||||||
margin-bottom: $spacing-small;
|
margin-bottom: $spacing-small;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.space-top-large {
|
||||||
|
margin-top: $spacing-large;
|
||||||
|
}
|
||||||
|
|
||||||
.space-top-medium {
|
.space-top-medium {
|
||||||
margin-top: $spacing-medium;
|
margin-top: $spacing-medium;
|
||||||
}
|
}
|
||||||
@ -42,6 +46,10 @@ $spacing-tiny: 5px;
|
|||||||
margin-bottom: $spacing-large;
|
margin-bottom: $spacing-large;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.space-bottom-medium {
|
||||||
|
margin-bottom: $spacing-medium;
|
||||||
|
}
|
||||||
|
|
||||||
.space-bottom-large {
|
.space-bottom-large {
|
||||||
margin-bottom: $spacing-large;
|
margin-bottom: $spacing-large;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user