Commit d25b5b4b authored by Stavros Sachtouris's avatar Stavros Sachtouris Committed by Giorgos Korfiatis
Browse files

Logout remote connection after getting credentials

When a user clicks on "Get credentials", a new window appears
where users can authenticate themselves. It is a remote window
implementing whatever mechanism the target cloud implements.

When the authentication window opens, the "Get credentials"
button disappears.

After the authentication is complete or canceled, the following
operations take place synchronously and the order they appear
bellow:
- if the authentication window is open, close it
- remove all cookies related to the account and pithos UI URLs
- Reactivate the "Get credentials" button.
parent 9fd926d6
__version__ = '0.1' __version__ = '0.1'
import cmd import cmd
import sys import sys
import logging import logging
import json from agkyra.syncer import setup, syncer
from titanic import setup, syncer from agkyra.syncer.pithos_client import PithosFileClient
from titanic.pithos_client import PithosFileClient from agkyra.syncer.localfs_client import LocalfsFileClient
from titanic.localfs_client import FilesystemFileClient from agkyra import config
import config
# from config import AgkyraConfig # from config import AgkyraConfig
logging.basicConfig(filename='agkyra.log', level=logging.DEBUG)
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
LOG.addHandler(logging.FileHandler('%s/agkyra.log' % config.AGKYRA_DIR))
LOG.setLevel(logging.CRITICAL)
SYNCER_LOG = logging.getLogger('agkyra.syncer')
SYNCER_LOG.addHandler(logging.FileHandler('%s/agkyra.log' % config.AGKYRA_DIR))
SYNCER_LOG.setLevel(logging.CRITICAL)
setup.GLOBAL_SETTINGS_NAME = config.AGKYRA_DIR setup.GLOBAL_SETTINGS_NAME = config.AGKYRA_DIR
...@@ -41,11 +45,12 @@ class AgkyraCLI(cmd.Cmd): ...@@ -41,11 +45,12 @@ class AgkyraCLI(cmd.Cmd):
# Init syncer # Init syncer
master = PithosFileClient(self.settings) master = PithosFileClient(self.settings)
slave = FilesystemFileClient(self.settings) slave = LocalfsFileClient(self.settings)
self.syncer = syncer.FileSyncer(self.settings, master, slave) self.syncer = syncer.FileSyncer(self.settings, master, slave)
def preloop(self): def preloop(self):
"""This runs when the shell loads""" """This runs when the shell loads"""
print 'Loading Agkyra (sometimes this takes a while)'
if not self.is_shell: if not self.is_shell:
self.is_shell = True self.is_shell = True
self.prompt = '\xe2\x9a\x93 ' self.prompt = '\xe2\x9a\x93 '
...@@ -128,14 +133,20 @@ class AgkyraCLI(cmd.Cmd): ...@@ -128,14 +133,20 @@ class AgkyraCLI(cmd.Cmd):
def do_start(self, line): def do_start(self, line):
"""Start syncing""" """Start syncing"""
self.syncer.run() if not getattr(self, '_syncer_initialized', False):
self.syncer.probe_and_sync_all()
self._syncer_initialized = True
if self.syncer.paused:
self.syncer.start_decide()
def do_pause(self, line): def do_pause(self, line):
"""Pause syncing""" """Pause syncing"""
if not self.syncer.paused:
self.syncer.pause_decide()
def do_status(self, line): def do_status(self, line):
"""Get current status (running/paused, progress)""" """Get current status (running/paused, progress)"""
print 'I have no idea' print 'paused' if self.syncer.paused else 'running'
# def do_shell(self, line): # def do_shell(self, line):
# """Run system, shell commands""" # """Run system, shell commands"""
...@@ -181,4 +192,4 @@ class AgkyraCLI(cmd.Cmd): ...@@ -181,4 +192,4 @@ class AgkyraCLI(cmd.Cmd):
# AgkyraCLI().run_onecmd(sys.argv) # AgkyraCLI().run_onecmd(sys.argv)
# or run a shell with # or run a shell with
AgkyraCLI().cmdloop() # AgkyraCLI().cmdloop()
from wsgiref.simple_server import make_server from wsgiref.simple_server import make_server
# from ws4py.websocket import EchoWebSocket # from ws4py.websocket import EchoWebSocket
from protocol import WebSocketProtocol from agkyra.protocol import WebSocketProtocol
from ws4py.server.wsgirefserver import WSGIServer, WebSocketWSGIRequestHandler from ws4py.server.wsgirefserver import WSGIServer, WebSocketWSGIRequestHandler
from ws4py.server.wsgiutils import WebSocketWSGIApplication from ws4py.server.wsgiutils import WebSocketWSGIApplication
from ws4py.client import WebSocketBaseClient from ws4py.client import WebSocketBaseClient
...@@ -20,11 +20,10 @@ LOG = logging.getLogger(__name__) ...@@ -20,11 +20,10 @@ LOG = logging.getLogger(__name__)
class GUI(WebSocketBaseClient): class GUI(WebSocketBaseClient):
"""Launch the GUI when the helper server is ready""" """Launch the GUI when the helper server is ready"""
def __init__(self, addr, gui_exec_path, gui_id): def __init__(self, addr, gui_id):
"""Initialize the GUI Launcher""" """Initialize the GUI Launcher"""
super(GUI, self).__init__(addr) super(GUI, self).__init__(addr)
self.addr = addr self.addr = addr
self.gui_exec_path = gui_exec_path
self.gui_id = gui_id self.gui_id = gui_id
self.start = self.connect self.start = self.connect
...@@ -39,11 +38,10 @@ class GUI(WebSocketBaseClient): ...@@ -39,11 +38,10 @@ class GUI(WebSocketBaseClient):
with NamedTemporaryFile(delete=False) as fp: with NamedTemporaryFile(delete=False) as fp:
json.dump(dict(gui_id=self.gui_id, address=self.addr), fp) json.dump(dict(gui_id=self.gui_id, address=self.addr), fp)
# subprocess.call blocks the execution # subprocess.call blocks the execution
LOG.debug('RUN: %s %s' % (self.gui_exec_path, fp.name)) LOG.debug('RUN: %s' % (fp.name))
subprocess.call([ subprocess.call([
'/home/saxtouri/node-webkit-v0.11.6-linux-x64/nw', abspath('agkyra/nwjs/nw'),
# self.gui_exec_path, abspath('agkyra/gui.nw'),
abspath('gui/gui.nw'),
fp.name, fp.name,
'--data-path', abspath('~/.agkyra')]) '--data-path', abspath('~/.agkyra')])
LOG.debug('GUI process closed, remove temp file') LOG.debug('GUI process closed, remove temp file')
...@@ -85,11 +83,11 @@ class HelperServer(object): ...@@ -85,11 +83,11 @@ class HelperServer(object):
t.join() t.join()
def run(gui_exec_path): def run():
"""Prepare helper and GUI and run them in the proper order""" """Prepare helper and GUI and run them in the proper order"""
server = HelperServer() server = HelperServer()
addr = 'ws://localhost:%s' % server.port addr = 'ws://localhost:%s' % server.port
gui = GUI(addr, gui_exec_path, server.gui_id) gui = GUI(addr, server.gui_id)
LOG.info('Start helper server') LOG.info('Start helper server')
server.start() server.start()
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
<body> <body>
<script src="protocol.js"></script> <script src="protocol.js"></script>
<script src="settings.js"></script> <script src="settings.js"></script>
<script src="static/js/jquery.js"></script>
<script type="text/javascript"> <script type="text/javascript">
// Setup GUI // Setup GUI
...@@ -42,7 +43,7 @@ menu.append(new gui.MenuItem({type: 'separator'})); ...@@ -42,7 +43,7 @@ menu.append(new gui.MenuItem({type: 'separator'}));
pause_item = new gui.MenuItem({ pause_item = new gui.MenuItem({
// pause menu item // pause menu item
icon: 'images/play_pause.png', icon: 'images/play_pause.png',
label: '', label: 'NOT READY',
type: 'normal', type: 'normal',
click: function() { click: function() {
if (paused) {post_start(socket);} else {post_pause(socket);} if (paused) {post_start(socket);} else {post_pause(socket);}
...@@ -51,84 +52,42 @@ pause_item = new gui.MenuItem({ ...@@ -51,84 +52,42 @@ pause_item = new gui.MenuItem({
pause_item.enabled = false; pause_item.enabled = false;
menu.append(pause_item); menu.append(pause_item);
// Update progress
window.setInterval(function() {
var status = globals['status'];
var new_progress = progress_item.label;
var new_pause = pause_item.label;
var menu_modified = false;
if (status['paused'] !== null) {
switch(pause_item.label) {
case pause_syncing: if (status['paused']) {
// Update to "Paused - start syncing"
paused = true;
new_pause = start_syncing;
menu_modified = true;
} // else continue syncing
new_progress = status['progress'] + '%' + ' synced';
break;
case start_syncing: if (status['paused']) return;
// else update to "Syncing - pause syncing"
paused = false;
new_pause = pause_syncing;
new_progress = status['progress'] + '%' + ' synced';
menu_modified = true;
break;
default:
if (status['paused']) {new_pause = start_syncing; paused=true;}
else {new_pause = pause_syncing; paused=false;}
new_progress = status['progress'] + '%' + ' synced';
pause_item.enabled = true;
menu_modified = true;
}
}
if (new_pause != pause_item.label) {
pause_item.label = new_pause;
menu_modified = true;
}
if (new_progress != progress_item.label) {
progress_item.label = new_progress;
menu_modified = true;
}
if (menu_modified) {
if (paused) progress_item.label += ' - paused';
tray.menu = menu;
}
get_status(socket);
}, 1500);
// Menu actions contents // Menu actions contents
menu.append(new gui.MenuItem({ var local_folder_menu = new gui.MenuItem({
label: 'Open local folder', label: 'Open local folder',
icon: 'images/folder.png', icon: 'images/folder.png',
enabled: false,
click: function () { click: function () {
var dir = globals['settings']['directory']; var dir = globals['settings']['directory'];
console.log('Open ' + dir); console.log('Open ' + dir);
gui.Shell.showItemInFolder(dir); gui.Shell.showItemInFolder(dir);
} }
})); })
menu.append(local_folder_menu);
menu.append(new gui.MenuItem({ var pithos_page_menu = new gui.MenuItem({
label: 'Launch Pithos+ page', label: 'Launch Pithos+ page',
icon: 'images/pithos.png', icon: 'images/pithos.png',
enabled: false,
click: function () { click: function () {
var pithos_ui = globals['settings']['pithos_ui']; console.log('Visit ' + get_pithos_ui());
console.log('Visit ' + pithos_ui); gui.Shell.openExternal(get_pithos_ui());
gui.Shell.openExternal(pithos_ui);
} }
})); });
menu.append(pithos_page_menu);
// Settings and About // Settings and About
menu.append(new gui.MenuItem({type: 'separator'})); menu.append(new gui.MenuItem({type: 'separator'}));
menu.append(new gui.MenuItem({ var settings_menu = new gui.MenuItem({
label: 'Settings', label: 'Settings',
icon: 'images/settings.png', icon: 'images/settings.png',
enabled: false,
click: function () { click: function () {
export_settings(globals.settings); export_settings(globals.settings);
if (windows['settings']) windows['settings'].close(); if (windows['settings']) windows['settings'].close();
windows['settings'] = gui.Window.open("settings.html", { windows['settings'] = gui.Window.open("settings.html", {
toolbar: false, focus: true, toolbar: false, focus: true,
width: 841, height: 520, width: 841, height: 520
}); });
windows['settings'].on('closed', function() { windows['settings'].on('closed', function() {
new_settings = import_settings(); new_settings = import_settings();
...@@ -137,7 +96,8 @@ menu.append(new gui.MenuItem({ ...@@ -137,7 +96,8 @@ menu.append(new gui.MenuItem({
get_settings(socket); get_settings(socket);
}); });
}, },
})); });
menu.append(settings_menu);
menu.append(new gui.MenuItem({ menu.append(new gui.MenuItem({
label: 'About', label: 'About',
...@@ -159,6 +119,79 @@ menu.append(new gui.MenuItem({ ...@@ -159,6 +119,79 @@ menu.append(new gui.MenuItem({
click: function() {post_shutdown(socket);} click: function() {post_shutdown(socket);}
})); }));
// Update progress
var client_ready = false;
window.setInterval(function() {
var menu_modified = false;
if (!client_ready) {
if (!globals.authenticated) return;
client_ready = true;
}
if (client_ready) {
if (!settings_menu.enabled) {
if (globals.settings.url) refresh_endpoints(globals.settings.url);
settings_menu.enabled = true;
tray.menu = menu;
}
if (globals.settings.url && !pithos_page_menu.enabled) {
if (get_pithos_ui() != null) {
pithos_page_menu.enabled = true;
tray.menu = menu;
} else { refresh_endpoints(globals.settings.url); }
}
if (!local_folder_menu.enabled) {
if (globals.settings.directory) {
local_folder_menu.enabled = true;
tray.menu = menu;
}
}
}
var status = globals['status'];
var new_progress = progress_item.label;
var new_pause = pause_item.label;
if (status['paused'] !== null) {
switch(pause_item.label) {
case pause_syncing: if (status['paused']) {
// Update to "Paused - start syncing"
paused = true;
new_pause = start_syncing;
menu_modified = true;
} // else continue syncing
new_progress = status['progress'] + '%' + ' synced';
break;
case start_syncing: if (status['paused']) return;
// else update to "Syncing - pause syncing"
paused = false;
new_pause = pause_syncing;
new_progress = status['progress'] + '%' + ' synced';
menu_modified = true;
break;
default:
if (status['paused']) {new_pause = start_syncing; paused=true;}
else {new_pause = pause_syncing; paused=false;}
new_progress = status['progress'] + '%' + ' synced';
pause_item.enabled = true;
menu_modified = true;
}
}
if (new_pause != pause_item.label) {
pause_item.label = new_pause;
menu_modified = true;
}
if (new_progress != progress_item.label) {
progress_item.label = new_progress;
menu_modified = true;
}
if (menu_modified) {
if (paused) progress_item.label += ' - paused';
tray.menu = menu;
}
get_status(socket);
}, 1500);
tray.menu = menu; tray.menu = menu;
</script> </script>
......
...@@ -16,10 +16,10 @@ var globals = { ...@@ -16,10 +16,10 @@ var globals = {
'url': null, 'url': null,
'container': null, 'container': null,
'directory': null, 'directory': null,
'pithos_ui': null,
'exclude': null 'exclude': null
}, },
'status': {"progress": null, "paused": null} 'status': {"progress": null, "paused": null},
'authenticated': false,
} }
// Protocol: requests ::: responses // Protocol: requests ::: responses
...@@ -65,12 +65,13 @@ socket.onopen = function() { ...@@ -65,12 +65,13 @@ socket.onopen = function() {
} }
socket.onmessage = function(e) { socket.onmessage = function(e) {
var r = JSON.parse(e.data) var r = JSON.parse(e.data)
//console.log('RECV: ' + r['action']) console.log('RECV: ' + r['action'])
switch(r['action']) { switch(r['action']) {
case 'post gui_id': case 'post gui_id':
if (r['ACCEPTED'] === 202) { if (r['ACCEPTED'] === 202) {
get_settings(this); get_settings(this);
get_status(this); get_status(this);
globals.authenticated = true;
} else { } else {
console.log('Helper: ' + JSON.stringify(r)); console.log('Helper: ' + JSON.stringify(r));
closeWindows(); closeWindows();
...@@ -85,7 +86,7 @@ socket.onmessage = function(e) { ...@@ -85,7 +86,7 @@ socket.onmessage = function(e) {
} }
break; break;
case 'get settings': case 'get settings':
console.log(r); // console.log(r);
globals['settings'] = r; globals['settings'] = r;
break; break;
case 'put settings': case 'put settings':
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
<script type="text/javascript"> <script type="text/javascript">
var fs = require('fs'); var fs = require('fs');
var exclude = null; var exclude = null;
remove_tokens = null;
$(document).ready(function() { $(document).ready(function() {
var url = get_setting('url'); var url = get_setting('url');
if (url) $('#cloud-url').val(url); if (url) $('#cloud-url').val(url);
...@@ -18,16 +19,11 @@ ...@@ -18,16 +19,11 @@
if (container) $('#container').val(container); if (container) $('#container').val(container);
var directory = get_setting('directory'); var directory = get_setting('directory');
if (directory) $('#directory').html(directory); if (directory) $('#directory').html(directory);
exclude = get_setting('exclude'); var exclude = get_setting('exclude');
if (exclude) $('#exclude').val( if (exclude) try {
fs.readFileSync(exclude, encoding='utf-8')); $('#exclude').val(
var pithos_ui = get_setting('pithos_ui'); fs.readFileSync(exclude, encoding='utf-8'));
if (pithos_ui) { } catch (err) {console.log(err);}
$('#get_creds').show();
} else {
console.log('No pithos view, remove credential button');
$('#get_creds').hide();
}
}); });
function update_exclude(new_content) { function update_exclude(new_content) {
...@@ -45,30 +41,79 @@ ...@@ -45,30 +41,79 @@
//set_setting('token', $(this).val()) //set_setting('token', $(this).val())
} }
function remove_cookies(win, url) {
var removed_at_least_one = false
win.cookies.getAll({url: url}, function(cookies) {
$.each(cookies, function(i, cookie) {
console.log('I have a cookie to remove ' + cookie.name + ', ' + cookie.domain);
win.cookies.remove({url: url, name: cookie.name} );
removed_at_least_one = true;
});
});
return removed_at_least_one;
}
var gui = require('nw.gui'); var gui = require('nw.gui');
function extract_cookie(url) { var cred_win = null;
var logout_win = null;
var can_close_cred = false;
var got_cookie = false;
var can_close_logout = false;
var show_creds = true;
function get_credentials() {
var cookie_name = '_pithos2_a'; var cookie_name = '_pithos2_a';
var logout_url = 'https://accounts.okeanos.grnet.gr/ui/logout' var lurl = get_account_ui() + '/logout?next=' + get_pithos_ui()
var w = gui.Window.open(logout_url, {
focus: false, width: 20, height: 20 show_creds = false;
}); $('#get_creds').hide();
w.close();
var w = gui.Window.open(logout_url, { got_cookie = false;
focus: true, width: 520, height: 920 cred_win = gui.Window.open(lurl, {
focus: true, width: 820, height: 580, toolbar: false
}); });
w.cookies.onChanged.addListener(function(info) {
cred_win.cookies.onChanged.addListener(function(info) {
if (info.cookie.name === cookie_name) { if (info.cookie.name === cookie_name) {
console.log('Succesfully logged in'); console.log('Succesfully logged in');
extract_credentials(info.cookie); extract_credentials(info.cookie);
w.close(); got_cookie = true;
} }
}); });
} cred_win.on('loaded', function() {
if (got_cookie) can_close_cred = true;
});
function get_credentials() { cred_win.on('closed', function() {
var pithos_ui = get_setting('pithos_ui'); logout_win = gui.Window.open(
extract_cookie(pithos_ui); get_account_ui() + '/logout',
{focus: true, width:20, height: 20 });
logout_win.hide();
logout_win.on('loaded', function() {
while(
remove_cookies(logout_win, get_account_ui()) ||
remove_cookies(logout_win, get_pithos_ui())) {}
can_close_logout = true;
});
});
} }
window.setInterval(function() {
// Refresh get_creds visibility, until refresh_endpoints
// changes are in effect
if (get_pithos_ui() && show_creds) {
$('#get_creds').show();
} else {$('#get_creds').hide(); }
// Garbage collector
if (can_close_logout) {
can_close_logout = false;
logout_win.close();
show_creds = true;
}
else if (can_close_cred) {
can_close_cred = false;
cred_win.close();
}
}, 500);
</script> </script>
</head> </head>
<body> <body>
...@@ -88,7 +133,7 @@ ...@@ -88,7 +133,7 @@
</div>