mirror of
https://github.com/nicolargo/glances.git
synced 2024-12-01 22:14:06 +03:00
Merge branch 'feature/savepassword' into develop
This commit is contained in:
commit
621cd74652
@ -117,10 +117,10 @@ class GlancesMain(object):
|
||||
# Server option
|
||||
parser.add_argument('-p', '--port', default=self.server_port, type=int, dest='port',
|
||||
help=_('define the client/server TCP port [default: %d]') % self.server_port)
|
||||
parser.add_argument('-P', '--password', dest='password_arg',
|
||||
help=_('old method to define a client/server password'))
|
||||
parser.add_argument('--password-badidea', dest='password_arg',
|
||||
help=_('Define password from the command line'))
|
||||
parser.add_argument('--password', action='store_true', default=False, dest='password_prompt',
|
||||
help=_('define a client/server password from the prompt'))
|
||||
help=_('define a client/server password from the prompt or file'))
|
||||
parser.add_argument('-s', '--server', action='store_true', default=False,
|
||||
dest='server', help=_('run Glances in server mode'))
|
||||
parser.add_argument('--snmp-community', default='public', dest='snmp_community',
|
||||
@ -160,10 +160,13 @@ class GlancesMain(object):
|
||||
# Server or client login/password
|
||||
args.username = self.username
|
||||
if args.password_arg is not None:
|
||||
# Password is passed as an argument
|
||||
args.password = args.password_arg
|
||||
from hashlib import sha256
|
||||
# Password is given as an argument
|
||||
# Hash with SHA256
|
||||
# Only the SHA will be transmit on the network
|
||||
args.password = sha256(args.password_arg).hexdigest()
|
||||
elif args.password_prompt:
|
||||
# Interactive password
|
||||
# Interactive or file password
|
||||
if args.server:
|
||||
args.password = self.__get_password(
|
||||
description=_("Define the password for the Glances server"),
|
||||
@ -171,7 +174,7 @@ class GlancesMain(object):
|
||||
elif args.client:
|
||||
args.password = self.__get_password(
|
||||
description=_("Enter the Glances server password"),
|
||||
confirm=False)
|
||||
clear=True)
|
||||
else:
|
||||
# Default is no password
|
||||
args.password = self.password
|
||||
@ -183,12 +186,6 @@ class GlancesMain(object):
|
||||
if args.client is not None:
|
||||
self.client_tag = True
|
||||
self.server_ip = args.client
|
||||
|
||||
# if args.output is not None:
|
||||
# setattr(self, args.output.lower() + '_tag', True)
|
||||
# if args.file is not None:
|
||||
# output_file = args.file
|
||||
# output_folder = args.file
|
||||
# /!!!
|
||||
|
||||
# Interactive cmds like CLI args?
|
||||
@ -201,26 +198,27 @@ class GlancesMain(object):
|
||||
|
||||
return args
|
||||
|
||||
def __get_password(self, description='', confirm=False):
|
||||
def __hash_password(self, plain_password):
|
||||
"""
|
||||
Read a password from the command line (with confirmation if confirm = True)
|
||||
Hash a plain password and return the hashed one
|
||||
"""
|
||||
import getpass
|
||||
from glances.core.glances_password import glancesPassword
|
||||
|
||||
if description != '':
|
||||
sys.stdout.write("%s\n" % description)
|
||||
password = glancesPassword()
|
||||
|
||||
password1 = getpass.getpass(_("Password: "))
|
||||
if confirm:
|
||||
password2 = getpass.getpass(_("Password (confirm): "))
|
||||
else:
|
||||
return password1
|
||||
return password.hash_password(plain_password)
|
||||
|
||||
if password1 == password2:
|
||||
return password1
|
||||
else:
|
||||
sys.stdout.write(_("[Warning] Passwords did not match, please try again...\n"))
|
||||
return self.__get_password(description=description, confirm=confirm)
|
||||
def __get_password(self, description='', confirm=False, clear=False):
|
||||
"""
|
||||
Read a password from the command line
|
||||
- with confirmation if confirm = True
|
||||
- plain (clear password) if clear = True
|
||||
"""
|
||||
from glances.core.glances_password import glancesPassword
|
||||
|
||||
password = glancesPassword()
|
||||
|
||||
return password.get_password(description, confirm, clear)
|
||||
|
||||
def is_standalone(self):
|
||||
"""
|
||||
|
181
glances/core/glances_password.py
Normal file
181
glances/core/glances_password.py
Normal file
@ -0,0 +1,181 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# Copyright (C) 2014 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/>.
|
||||
|
||||
# Import system libs
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
import hashlib
|
||||
import getpass
|
||||
|
||||
# Import Glances lib
|
||||
from glances.core.glances_globals import (
|
||||
__appname__,
|
||||
is_bsd,
|
||||
is_linux,
|
||||
is_mac,
|
||||
is_windows
|
||||
)
|
||||
|
||||
|
||||
class glancesPassword:
|
||||
"""
|
||||
Manage password
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.password_path = self.get_password_path()
|
||||
self.password_filename = __appname__ + '.pwd'
|
||||
self.password_filepath = os.path.join(self.password_path, self.password_filename)
|
||||
|
||||
def get_password_path(self):
|
||||
"""
|
||||
Get the path where the password file will be stored
|
||||
On Linux and BSD the right place should be $XDG_DATA_HOME aka $HOME/.local/share/glances/foo.
|
||||
On OS X: the right place is under user's Library folder aka $HOME/Library/glances/foo
|
||||
On Windows: os.environ['APPDATA']+'/glances/'+foo
|
||||
"""
|
||||
|
||||
# Get the system application data path for the current user
|
||||
if is_linux or is_bsd:
|
||||
app_path = os.environ.get('XDG_CONFIG_HOME') or os.path.expanduser('~/.config')
|
||||
elif is_mac:
|
||||
app_path = os.path.join(os.environ.get('HOME'), 'Library')
|
||||
elif is_windows:
|
||||
app_path = os.environ.get('APPDATA')
|
||||
else:
|
||||
app_path = '.'
|
||||
|
||||
# Append the Glances folder
|
||||
app_path = os.path.join(app_path, __appname__)
|
||||
|
||||
return app_path
|
||||
|
||||
def get_hash(self, salt, plain_password):
|
||||
"""
|
||||
Return the hashed password SHA265 + salt
|
||||
"""
|
||||
return hashlib.sha256(salt.encode() + plain_password.encode()).hexdigest()
|
||||
|
||||
def hash_password(self, plain_password):
|
||||
"""
|
||||
Hash password with a salt based on UUID
|
||||
"""
|
||||
salt = uuid.uuid4().hex
|
||||
encrypted_password = self.get_hash(salt, plain_password)
|
||||
return salt + '$' + encrypted_password
|
||||
|
||||
def check_password(self, hashed_password, plain_password):
|
||||
"""
|
||||
Encode the plain_password with the salt of the hashed_password
|
||||
and return the comparaison with the encrypted_password
|
||||
"""
|
||||
salt, encrypted_password = hashed_password.split('$')
|
||||
re_encrypted_password = self.get_hash(salt, plain_password)
|
||||
return encrypted_password == re_encrypted_password
|
||||
|
||||
def get_password(self, description='', confirm=False, clear=False):
|
||||
"""
|
||||
For Glances server, get the password (confirm=True, clear=False)
|
||||
1) from the password file (if the file exist)
|
||||
2) from the CLI
|
||||
Optinnaly: save the password to a file (hashed with SHA256 + SALT)
|
||||
|
||||
For Glances client, get the password (confirm=False, clear=True)
|
||||
1) From the CLI
|
||||
2) The password is hashed with SHA256 (only SHA string transit thrught the network)
|
||||
"""
|
||||
|
||||
if os.path.exists(self.password_filepath) and not clear:
|
||||
# If the password file exist then use it
|
||||
sys.stdout.write(_("[Info] Read password from file %s\n") % self.password_filepath)
|
||||
password = self.load_password()
|
||||
else:
|
||||
# Else enter the password from the command line
|
||||
if description != '':
|
||||
sys.stdout.write("%s\n" % description)
|
||||
|
||||
# password_plain is the password MD5
|
||||
# password_hashed is the hashed password
|
||||
password_sha = hashlib.sha256(getpass.getpass(_("Password: "))).hexdigest()
|
||||
password_hashed = self.hash_password(password_sha)
|
||||
if confirm:
|
||||
# password_confirm is the clear password (only used to compare)
|
||||
password_confirm = hashlib.sha256(getpass.getpass(_("Password (confirm): "))).hexdigest()
|
||||
|
||||
if not self.check_password(password_hashed, password_confirm):
|
||||
sys.stdout.write(_("[Error] Sorry, but passwords did not match...\n"))
|
||||
sys.exit(1)
|
||||
|
||||
# Return the clear or hashed password
|
||||
if clear:
|
||||
password = password_sha
|
||||
else:
|
||||
password = password_hashed
|
||||
|
||||
# Save the hashed password to the password file
|
||||
if not clear:
|
||||
save_input = raw_input(_("Do you want to save the password (Yes|No) ? "))
|
||||
if len(save_input) > 0 and save_input[0].upper() == _('Y'):
|
||||
self.save_password(password_hashed)
|
||||
|
||||
return password
|
||||
|
||||
def save_password(self, hashed_password):
|
||||
"""
|
||||
Save the hashed password to the Glances appdata folder
|
||||
"""
|
||||
|
||||
# Check if the Glances appdata folder already exist
|
||||
if not os.path.exists(self.password_path):
|
||||
# Create the Glances appdata folder
|
||||
try:
|
||||
os.mkdir(self.password_path)
|
||||
except Exception as e:
|
||||
sys.stdout.write(_("[Warning] Glances application data folder can not be created (%s)\n") % e)
|
||||
return
|
||||
|
||||
# Create/overwrite the password file to the Glances application data folder
|
||||
try:
|
||||
file_pwd = open(self.password_filepath, 'w')
|
||||
except Exception as e:
|
||||
sys.stdout.write(_("[Warning] Glances wan not create the password file (%s)\n") % e)
|
||||
return
|
||||
|
||||
# Generate the password file
|
||||
file_pwd.write(hashed_password)
|
||||
file_pwd.close()
|
||||
|
||||
def load_password(self):
|
||||
"""
|
||||
Load the hashed password from the Glances appdata folder
|
||||
"""
|
||||
|
||||
# Create/overwrite the password file to the Glances application data folder
|
||||
try:
|
||||
file_pwd = open(self.password_filepath, 'r')
|
||||
except Exception as e:
|
||||
sys.stdout.write(_("[Warning] Glances wan not read the password file (%s)\n") % e)
|
||||
return None
|
||||
|
||||
# Read the password file
|
||||
hashed_password = file_pwd.read()
|
||||
file_pwd.close()
|
||||
|
||||
return hashed_password
|
@ -22,7 +22,6 @@ import json
|
||||
import socket
|
||||
import sys
|
||||
from base64 import b64decode
|
||||
from hashlib import md5
|
||||
try:
|
||||
from xmlrpc.server import SimpleXMLRPCRequestHandler
|
||||
from xmlrpc.server import SimpleXMLRPCServer
|
||||
@ -79,9 +78,13 @@ class GlancesXMLRPCHandler(SimpleXMLRPCRequestHandler):
|
||||
def check_user(self, username, password):
|
||||
# Check username and password in the dictionnary
|
||||
if username in self.server.user_dict:
|
||||
if self.server.user_dict[username] == md5(password).hexdigest():
|
||||
return True
|
||||
return False
|
||||
from glances.core.glances_password import glancesPassword
|
||||
|
||||
pwd = glancesPassword()
|
||||
|
||||
return pwd.check_password(self.server.user_dict[username], password)
|
||||
else:
|
||||
return False
|
||||
|
||||
def parse_request(self):
|
||||
if SimpleXMLRPCRequestHandler.parse_request(self):
|
||||
@ -203,7 +206,7 @@ class GlancesServer():
|
||||
sys.exit(2)
|
||||
|
||||
# The users dict
|
||||
# username / MD5 password couple
|
||||
# username / password couple
|
||||
# By default, no auth is needed
|
||||
self.server.user_dict = {}
|
||||
self.server.isAuth = False
|
||||
@ -216,7 +219,7 @@ class GlancesServer():
|
||||
"""
|
||||
Add an user to the dictionnary
|
||||
"""
|
||||
self.server.user_dict[username] = md5(password).hexdigest()
|
||||
self.server.user_dict[username] = password
|
||||
self.server.isAuth = True
|
||||
|
||||
def serve_forever(self):
|
||||
|
Loading…
Reference in New Issue
Block a user