1. Added Master Password to increase the security of saved passwords. Fixes #4184

2. In server(web) mode, update all the saved server credentials when user password is changed. Fixes #3377
This commit is contained in:
Aditya Toshniwal
2019-05-28 12:00:18 +05:30
committed by Akshay Joshi
parent 6f0eafb223
commit dfa892d2a2
44 changed files with 1509 additions and 416 deletions

View File

@@ -26,6 +26,8 @@ import config
from config import PG_DEFAULT_DRIVER
from pgadmin.model import db, Server, ServerGroup, User
from pgadmin.utils.driver import get_driver
from pgadmin.utils.master_password import get_crypt_key
from pgadmin.utils.exception import CryptKeyMissing
def has_any(data, keys):
@@ -117,9 +119,16 @@ class ServerModule(sg.ServerGroupPluginModule):
driver = get_driver(PG_DEFAULT_DRIVER)
for server in servers:
manager = driver.connection_manager(server.id)
conn = manager.connection()
connected = conn.connected()
connected = False
manager = None
try:
manager = driver.connection_manager(server.id)
conn = manager.connection()
connected = conn.connected()
except CryptKeyMissing:
# show the nodes at least even if not able to connect.
pass
in_recovery = None
wal_paused = None
@@ -723,6 +732,11 @@ class ServerNode(PGChildNodeView):
request.data, encoding='utf-8'
)
# Get enc key
crypt_key_present, crypt_key = get_crypt_key()
if not crypt_key_present:
raise CryptKeyMissing
# Some fields can be provided with service file so they are optional
if 'service' in data and not data['service']:
required_args.extend([
@@ -807,7 +821,7 @@ class ServerNode(PGChildNodeView):
# login with password
have_password = True
password = data['password']
password = encrypt(password, current_user.password)
password = encrypt(password, crypt_key)
elif 'passfile' in data and data["passfile"] != '':
passfile = data['passfile']
setattr(server, 'passfile', passfile)
@@ -817,7 +831,7 @@ class ServerNode(PGChildNodeView):
have_tunnel_password = True
tunnel_password = data['tunnel_password']
tunnel_password = \
encrypt(tunnel_password, current_user.password)
encrypt(tunnel_password, crypt_key)
status, errmsg = conn.connect(
password=password,
@@ -998,6 +1012,11 @@ class ServerNode(PGChildNodeView):
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
conn = manager.connection()
# Get enc key
crypt_key_present, crypt_key = get_crypt_key()
if not crypt_key_present:
raise CryptKeyMissing
# If server using SSH Tunnel
if server.use_ssh_tunnel:
if 'tunnel_password' not in data:
@@ -1014,12 +1033,12 @@ class ServerNode(PGChildNodeView):
# Encrypt the password before saving with user's login
# password key.
try:
tunnel_password = encrypt(tunnel_password, user.password) \
tunnel_password = encrypt(tunnel_password, crypt_key) \
if tunnel_password is not None else \
server.tunnel_password
except Exception as e:
current_app.logger.exception(e)
return internal_server_error(errormsg=e.message)
return internal_server_error(errormsg=str(e))
if 'password' not in data:
conn_passwd = getattr(conn, 'password', None)
@@ -1038,11 +1057,11 @@ class ServerNode(PGChildNodeView):
# Encrypt the password before saving with user's login
# password key.
try:
password = encrypt(password, user.password) \
password = encrypt(password, crypt_key) \
if password is not None else server.password
except Exception as e:
current_app.logger.exception(e)
return internal_server_error(errormsg=e.message)
return internal_server_error(errormsg=str(e))
# Check do we need to prompt for the database server or ssh tunnel
# password or both. Return the password template in case password is
@@ -1235,6 +1254,7 @@ class ServerNode(PGChildNodeView):
"""
try:
data = json.loads(request.form['data'], encoding='utf-8')
crypt_key = get_crypt_key()[1]
# Fetch Server Details
server = Server.query.filter_by(id=sid).first()
@@ -1292,7 +1312,7 @@ class ServerNode(PGChildNodeView):
# Check against old password only if no pgpass file
if not is_passfile:
decrypted_password = decrypt(manager.password, user.password)
decrypted_password = decrypt(manager.password, crypt_key)
if isinstance(decrypted_password, bytes):
decrypted_password = decrypted_password.decode()
@@ -1328,7 +1348,7 @@ class ServerNode(PGChildNodeView):
# Store password in sqlite only if no pgpass file
if not is_passfile:
password = encrypt(data['newPassword'], user.password)
password = encrypt(data['newPassword'], crypt_key)
# Check if old password was stored in pgadmin4 sqlite database.
# If yes then update that password.
if server.password is not None and config.ALLOW_SAVE_PASSWORD:
@@ -1472,6 +1492,7 @@ class ServerNode(PGChildNodeView):
def get_response_for_password(self, server, status, prompt_password=False,
prompt_tunnel_password=False, errmsg=None):
if server.use_ssh_tunnel:
return make_json_response(
success=0,
@@ -1498,7 +1519,7 @@ class ServerNode(PGChildNodeView):
server_label=server.name,
username=server.username,
errmsg=errmsg,
_=gettext
_=gettext,
)
)