Building permission-checker for system functions

This commit is contained in:
James Barnsley
2018-04-23 09:07:48 +12:00
parent 5c1d559f3a
commit 97f3b0481e
7 changed files with 25494 additions and 23499 deletions

View File

@ -1 +1 @@
3.17.1
3.17.3

View File

@ -473,6 +473,7 @@ class IrisCore(object):
else:
return response
def get_version(self, *args, **kwargs):
callback = kwargs.get('callback', False)
url = 'https://pypi.python.org/pypi/Mopidy-Iris/json'
@ -504,11 +505,28 @@ class IrisCore(object):
else:
return response
def upgrade(self, *args, **kwargs):
logger.info("Upgrading")
callback = kwargs.get('callback', False)
try:
self.check_system_access()
except Exception, e:
logger.error(e)
error = {
'message': "Permission denied",
'description': str(e)
}
if (callback):
callback(False, error)
return
else:
return error
self.broadcast(data={
'method': "upgrading",
'params': {}
@ -516,24 +534,6 @@ class IrisCore(object):
# Run the system task
path = os.path.dirname(__file__)
# Make sure we can run as sudo without password
# TODO: Test cross-platform??
permission_check = str(subprocess.call(["sudo -n "+path+"/system.sh"], shell=True)).lower()
if "password is required" in permission_check:
logger.error("Upgrade failed: Permission denied. Password-less access to "+path+"/system.sh was refused. Check your /etc/sudoers file.")
error = {
'message': "Permission denied",
'description': "Password-less access to "+path+"/system.sh was refused. Check your /etc/sudoers file."
}
if (callback):
callback(False, error)
return
else:
return error
# Attempt the upgrade
upgrade_process = subprocess.Popen("sudo "+path+"/system.sh upgrade", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
@ -578,9 +578,25 @@ class IrisCore(object):
def restart(self, *args, **kwargs):
logger.info("Restarting")
callback = kwargs.get('callback', False)
try:
self.check_system_access()
except Exception, e:
logger.error(e)
error = {
'message': "Permission denied",
'description': str(e)
}
if (callback):
callback(False, error)
return
else:
return error
self.broadcast(data={
'method': "restarting",
'params': {}
@ -588,22 +604,6 @@ class IrisCore(object):
path = os.path.dirname(__file__)
# Make sure we can run as sudo without password
# TODO: Test cross-platform??
permission_check = str(subprocess.call(["sudo -n "+path+"/system.sh"], shell=True)).lower()
if "password is required" in permission_check:
error = {
'message': "Permission denied",
'data': {
'description': "Password-less access to "+path+"/system.sh was refused. Check your /etc/sudoers file."
}
}
if (callback):
callback(False, error)
return
else:
return error
subprocess.Popen(["sudo "+path+"/system.sh restart 0"], shell=True)
response = {
@ -1019,16 +1019,55 @@ class IrisCore(object):
##
# Simple test method
# Check if we have access to the system script (system.sh)
#
# @return boolean or exception
##
def check_system_access(self, *args, **kwargs):
callback = kwargs.get('callback', None)
# Run the system task
path = os.path.dirname(__file__)
# Attempt the upgrade
process = subprocess.Popen("sudo -n "+path+"/system.sh", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
result, error = process.communicate()
exitCode = process.wait()
if exitCode > 0:
raise Exception("Password-less access to "+path+"/system.sh was refused. Check your /etc/sudoers file.")
else:
return True
##
# Simple test method. Not for use in production for any purposes.
##
def test(self, *args, **kwargs):
callback = kwargs.get('callback', None)
data = kwargs.get('data', {})
if data and 'force_error' in data:
callback(False, {'message': "Could not sleep, forced error"})
try:
self.check_system_access()
except Exception, e:
logger.error(e)
error = {
'message': "Permission denied",
'description': str(e)
}
if (callback):
callback(False, error)
return
else:
return error
response = {
'message': "Permission granted"
}
if (callback):
callback(response)
return
else:
time.sleep(1)
callback({'message': "Slept for one second"}, False)
return
return response

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -73,7 +73,7 @@
// Release details
// These are automatically injected to built HTML
var build = "1524301303";
var build = "1524428502";
var version = "3.17.1";
// Construct the script tag

View File

@ -24,21 +24,31 @@ const PusherMiddleware = (function(){
console.log('Pusher log (incoming)', message);
}
// Pull our ID. JSON-RPC nests the ID under the error object,
// so make sure we handle that.
// TODO: Use this as our measure of a successful response vs error
var id = null;
if (message.id){
id = message.id;
} else if (message.error && message.error.id){
id = message.error.id;
}
// Response with request_id
if (message.id !== undefined && message.id){
if (id){
// Response matches a pending request
if (deferredRequests[message.id] !== undefined){
if (deferredRequests[id] !== undefined){
store.dispatch(uiActions.stopLoading(message.id));
store.dispatch(uiActions.stopLoading(id));
// Response is an error
if (message.error !== undefined){
deferredRequests[message.id].reject(message.error);
deferredRequests[id].reject(message.error);
// Successful response
} else {
deferredRequests[message.id].resolve(message.result);
deferredRequests[id].resolve(message.result);
}
// Hmm, the response doesn't appear to be for us?
@ -54,10 +64,10 @@ const PusherMiddleware = (function(){
// Broadcast of an error
if (message.error !== undefined){
store.dispatch(coreActions.handleException(
'Pusher: '+message.error.message,
message
message,
(message.error.data !== undefined && message.error.data.description !== undefined ? message.error.data.description : null)
));
} else {
@ -110,10 +120,6 @@ const PusherMiddleware = (function(){
const request = (store, method, params = null) => {
return new Promise((resolve, reject) => {
if (store.getState().ui.log_pusher){
console.log('Pusher log (outgoing)', {method: method, params: params});
}
var id = helpers.generateGuid();
var message = {
jsonrpc: '2.0',
@ -123,11 +129,16 @@ const PusherMiddleware = (function(){
if (params){
message.params = params;
}
if (store.getState().ui.log_pusher){
console.log('Pusher log (outgoing)', message);
}
socket.send(JSON.stringify(message));
store.dispatch(uiActions.startLoading(id, 'pusher_'+method));
// Start our 15 second timeout
// Start our 30 second timeout
var timeout = setTimeout(
function(){
store.dispatch(uiActions.stopLoading(id));
@ -499,7 +510,7 @@ const PusherMiddleware = (function(){
store.dispatch(mopidyActions.upgrading());
},
error => {
store.dispatch(uiActions.createNotification({content: error.message, type: 'bad'}));
store.dispatch(uiActions.createNotification({content: error.message, description: (error.description ? error.description : null), type: 'bad'}));
}
);
break;
@ -517,21 +528,25 @@ const PusherMiddleware = (function(){
// Set default country/locale (unless we've already been configured)
var spotify = store.getState().spotify;
var spotify_updated = false;
var spotify_updates = {};
if (!spotify.country && action.config.country){
spotify_updates.country = action.config.country;
spotify_updated = true;
}
if (!spotify.locale && action.config.locale){
spotify_updates.locale = action.config.locale;
spotify_updated = true;
}
if (action.config.spotify_authorization_url){
spotify_updates.authorization_url = action.config.authorization_url;
spotify_updated = true;
}
if (spotify_updates !== {}){
if (spotify_updated){
store.dispatch(spotifyActions.set(spotify_updates));
}

View File

@ -153,7 +153,7 @@ class Settings extends React.Component {
} else if (this.props.pusher.version.upgrade_available){
var upgrade_button = <button className="alternative" onClick={e => this.props.pusherActions.upgrade()}>Upgrade to { this.props.pusher.version.latest }</button>;
} else {
var upgrade_button = <button className="alternative" onClick={e => this.props.pusherActions.upgrade()}>Already up-to-date</button>;
var upgrade_button = null;
}
return (
@ -271,7 +271,7 @@ class Settings extends React.Component {
<div className="name">Version</div>
<div className="input">
<span className="text">
{this.props.pusher.version.current} installed {this.props.pusher.version.upgrade_available ? <span className="flag blue">Upgrade available</span> : null}
{this.props.pusher.version.current} installed {this.props.pusher.version.upgrade_available ? <span className="flag blue">Upgrade available</span> : <span className="flag grey">Up-to-date</span>}
</span>
</div>
</div>