Server password configuration for the browser mode #500

This commit is contained in:
nicolargo 2015-05-31 16:37:20 +02:00
parent 5e4c51652c
commit 4f41d8c062
6 changed files with 125 additions and 12 deletions

4
NEWS
View File

@ -5,7 +5,9 @@ Glances Version 2.x
Version 2.5
===========
...
Enhancements and new features:
* Server password configuration for the browser mode (issue #500)
Version 2.4.2
=============

View File

@ -153,7 +153,7 @@ mem_critical=90
#list_3_countmin=1
#[serverlist]
# Define the static server list
# Define the static servers list
#server_1_name=localhost
#server_1_alias=My local PC
#server_1_port=61209
@ -165,6 +165,15 @@ mem_critical=90
#server_4_name=pasbon
#server_4_port=61237
[passwords]
# Define the passwords list
# Syntax: host=password
# Where: host is the hostname
# password is the clear password
# Additionnaly (and optionnaly) a default password could be defined
#xps=abc
#default=defaultpassword
[influxdb]
host=localhost
port=8086

View File

@ -27,12 +27,14 @@ try:
except ImportError:
# Python 2
from xmlrpclib import ServerProxy, Fault, ProtocolError
from hashlib import sha256
# Import Glances libs
from glances.core.glances_autodiscover import GlancesAutoDiscoverServer
from glances.core.glances_client import GlancesClient, GlancesClientTransport
from glances.core.glances_logging import logger
from glances.core.glances_staticlist import GlancesStaticServer
from glances.core.glances_passwordlist import GlancesPassword
from glances.outputs.glances_curses import GlancesCursesBrowser
@ -44,9 +46,11 @@ class GlancesClientBrowser(object):
# Store the arg/config
self.args = args
self.config = config
self.static_server = None
self.password = None
# Init the static server list (if defined)
self.static_server = GlancesStaticServer(config=self.config)
# Load the configuration file
self.load()
# Start the autodiscover mode (Zeroconf listener)
if not self.args.disable_autodiscover:
@ -57,6 +61,14 @@ class GlancesClientBrowser(object):
# Init screen
self.screen = GlancesCursesBrowser(args=self.args)
def load(self):
"""Load server and password list from the confiuration file"""
# Init the static server list (if defined)
self.static_server = GlancesStaticServer(config=self.config)
# Init the password list (if defined)
self.password = GlancesPassword(config=self.config)
def get_servers_list(self):
"""Return the current server list (list of dict).
@ -75,6 +87,11 @@ class GlancesClientBrowser(object):
"""Return the URI for the given server dict."""
# Select the connection mode (with or without password)
if server['password'] != "":
if server['status'] == 'PROTECTED':
# Try with the preconfigure password (only if status is PROTECTED)
clear_password = self.password.get_password(server['name'])
if clear_password is not None:
server['password'] = self._encode_password(clear_password)
return 'http://{0}:{1}@{2}:{3}'.format(server['username'], server['password'],
server['ip'], server['port'])
else:
@ -165,14 +182,17 @@ class GlancesClientBrowser(object):
# A password is needed to access to the server's stats
if self.get_servers_list()[self.screen.active_server]['password'] is None:
from hashlib import sha256
# Display a popup to enter password
clear_password = self.screen.display_popup(
'Password needed for {0}: '.format(v['name']), is_input=True)
# Hash with SHA256
encoded_password = sha256(clear_password.encode('utf-8')).hexdigest()
# First of all, check if a password is available in the [passwords] section
clear_password = self.password.get_password(v['name'])
if clear_password is None \
or self.get_servers_list()[self.screen.active_server]['status'] == 'PROTECTED':
# Else, the password should be enter by the user
# Display a popup to enter password
clear_password = self.screen.display_popup(
'Password needed for {0}: '.format(v['name']), is_input=True)
# Store the password for the selected server
self.set_in_selected('password', encoded_password)
self.set_in_selected('password',
self._encode_password(clear_password))
# Display the Glance client on the selected server
logger.info("Connect Glances client to the {0} server".format(
@ -238,6 +258,11 @@ class GlancesClientBrowser(object):
else:
self.static_server.set_server(self.screen.active_server, key, value)
def _encode_password(self, clear_password):
"""Encode clear password using SHA256"""
# Hash with SHA256
return sha256(clear_password.encode('utf-8')).hexdigest()
def end(self):
"""End of the client browser session."""
self.screen.end()

View File

@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
# Copyright (C) 2015 Nicolargo <nicolas@nicolargo.com>
#
# Glances is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Glances is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Manage the Glances passwords list."""
# Import Glances libs
from glances.core.glances_logging import logger
class GlancesPassword(object):
"""Manage the Glances passwords list for the client|browser/server."""
_section = "passwords"
def __init__(self, config=None, args=None):
# password_dict is a dict (JSON compliant)
# {'host': 'password', ... }
# Load the configuration file
self._password_dict = self.load(config)
def load(self, config):
"""Load the password from the configuration file."""
password_dict = []
if config is None:
logger.warning("No configuration file available. Cannot load password list.")
elif not config.has_section(self._section):
logger.warning("No [%s] section in the configuration file. Cannot load password list." % self._section)
else:
logger.info("Start reading the [%s] section in the configuration file" % self._section)
password_dict = dict(config.items(self._section))
# Password list loaded
logger.info("%s password(s) loaded from the configuration file" % len(password_dict))
logger.debug("Password dictionary: %s" % password_dict)
return password_dict
def get_password(self, host=None):
"""
If host=None, return the current server list (dict).
Else, return the host's password (or the default one if defined or None)
"""
if host is None:
return self._password_dict
else:
try:
return self._password_dict[host]
except (KeyError, TypeError):
try:
return self._password_dict['default']
except (KeyError, TypeError):
return None
return None
def set_password(self, host, password):
"""Set a password for a specific host."""
self._password_dict[host] = password

View File

@ -59,6 +59,7 @@ class GlancesStaticServer(object):
if new_server['port'] is None:
new_server['port'] = 61209
new_server['username'] = 'glances'
# By default, try empty (aka no) password
new_server['password'] = ''
try:
new_server['ip'] = gethostbyname(new_server['name'])

View File

@ -1089,7 +1089,7 @@ class GlancesCursesBrowser(_GlancesCurses):
['load_min5', 'LOAD', 6],
['cpu_percent', 'CPU%', 5],
['mem_percent', 'MEM%', 5],
['status', 'STATUS', 8],
['status', 'STATUS', 9],
['ip', 'IP', 15],
# ['port', 'PORT', 5],
['hr_name', 'OS', 16],