Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 77 additions & 15 deletions mig/shared/griddaemons/login.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
Expand All @@ -20,7 +20,7 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

Check warning on line 23 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

line too long (81 > 80 characters)
#
# -- END_HEADER ---
#
Expand All @@ -28,8 +28,8 @@
"""MiG login daemon functions"""

from past.builtins import basestring
from builtins import object

Check failure on line 31 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

unused import 'object' (90% confidence)
import glob

Check failure on line 32 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

unused import 'glob' (90% confidence)
import logging
import os
import socket
Expand All @@ -43,7 +43,7 @@
from mig.shared.gdp.all import get_project_from_user_id
from mig.shared.sharelinks import extract_mode_id
from mig.shared.ssh import parse_pub_key
from mig.shared.useradm import ssh_authkeys, davs_authkeys, ftps_authkeys, \

Check failure on line 46 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

unused import 'lookup_client_id' (90% confidence)
https_authkeys, get_authkeys, ssh_authpasswords, davs_authpasswords, \
ftps_authpasswords, https_authpasswords, get_authpasswords, \
ssh_authdigests, davs_authdigests, ftps_authdigests, https_authdigests, \
Expand All @@ -55,8 +55,10 @@
# NOTE: auth keys file may easily contain only blank lines, so we decide to
# consider any such file of less than a 64 bytes invalid. ED25519 pub
# keys are typically around 80 bytes be careful not to go there.
# Hashed passwords are typically just short of 70 bytes.

min_pub_key_bytes = 64
min_authpasswords_bytes = min_authdigests_bytes = 64
min_authkeys_bytes = 64


class Login(object):
Expand Down Expand Up @@ -145,7 +147,7 @@
first.last_update = os.path.getmtime(authkeys_path)
changed_paths.append(authkeys_path)
elif os.path.exists(authkeys_path) and \
os.path.getsize(authkeys_path) >= min_pub_key_bytes and \
os.path.getsize(authkeys_path) >= min_authkeys_bytes and \
os.path.getmtime(authkeys_path) > any_last_update:
# logger.debug("found changed pub keys for %s" % username)
changed_paths.append(authkeys_path)
Expand Down Expand Up @@ -209,10 +211,12 @@
return changed_paths


def get_share_changes(conf, username, sharelink_path, authkeys_path):
def get_share_changes(conf, username, sharelink_path, authkeys_path,
authpasswords_path, authdigests_path):
"""Check if sharelink changed for username using the provided
sharelink_path file, authkeys_path file and the saved time stamp from
shares embedded in conf.
sharelink_path file, authkeys_path file, authpasswords_path file,
authdigests_path file and the saved time stamp from shares embedded in
conf.
Returns a list of changed sharelink files with the empty list if none
changed.
"""
Expand All @@ -222,6 +226,8 @@
creds_lock.acquire()
old_users = [i for i in conf['shares'] if i.username == username]
old_key_users = [i for i in old_users if i.public_key]
old_pw_users = [i for i in old_users if i.password]
old_digest_users = [i for i in old_users if i.digest]
if creds_lock:
creds_lock.release()
# We do not save share entry for key files without proper pub keys, so to
Expand All @@ -232,6 +238,10 @@
any_last_update = old_users[0].last_update
if old_key_users:
any_last_update = max(any_last_update, old_key_users[0].last_update)
if old_pw_users:
any_last_update = max(any_last_update, old_pw_users[0].last_update)
if old_digest_users:
any_last_update = max(any_last_update, old_digest_users[0].last_update)
changed_paths = []
if old_users:
first = old_users[0]
Expand All @@ -252,10 +262,36 @@
first.last_update = os.path.getmtime(authkeys_path)
changed_paths.append(authkeys_path)
elif os.path.exists(authkeys_path) and \
os.path.getsize(authkeys_path) >= min_pub_key_bytes and \
os.path.getsize(authkeys_path) >= min_authkeys_bytes and \
os.path.getmtime(authkeys_path) > any_last_update:
# logger.debug("found changed pub keys for %s" % username)
changed_paths.append(authkeys_path)
if conf["allow_password"]:
if old_pw_users:
first = old_pw_users[0]
if not os.path.exists(authpasswords_path):
changed_paths.append(authpasswords_path)
elif os.path.getmtime(authpasswords_path) > first.last_update:
first.last_update = os.path.getmtime(authpasswords_path)
changed_paths.append(authpasswords_path)
elif os.path.exists(authpasswords_path) and \
os.path.getsize(authpasswords_path) >= min_authpasswords_bytes and \

Check warning on line 278 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

line too long (84 > 80 characters)
os.path.getmtime(authpasswords_path) > any_last_update:
# logger.debug("found changed passwords for %s" % username)
changed_paths.append(authpasswords_path)
if conf["allow_digest"]:
if old_digest_users:
first = old_digest_users[0]
if not os.path.exists(authdigests_path):
changed_paths.append(authdigests_path)
elif os.path.getmtime(authdigests_path) > first.last_update:
first.last_update = os.path.getmtime(authdigests_path)
changed_paths.append(authdigests_path)
elif os.path.exists(authdigests_path) and \
os.path.getsize(authdigests_path) >= min_authdigests_bytes and \
os.path.getmtime(authdigests_path) > any_last_update:
# logger.debug("found changed digests for %s" % username)
changed_paths.append(authdigests_path)
return changed_paths


Expand Down Expand Up @@ -391,7 +427,7 @@
all_digests = []
# Clean up all old key entries for this user
conf['users'] = [i for i in conf['users']
if not i.username in user_logins or

Check warning on line 430 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

test for membership should be 'not in'
i.public_key is None]
elif auth_file == proto_authpasswords:
all_keys = []
Expand All @@ -407,7 +443,7 @@
all_digests = []
# Clean up all old password entries for this user
conf['users'] = [i for i in conf['users']
if not i.username in user_logins or

Check warning on line 446 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

test for membership should be 'not in'
i.password is None]
else:
all_keys = []
Expand All @@ -420,7 +456,7 @@
all_digests = []
# Clean up all old digest entries for this user
conf['users'] = [i for i in conf['users']
if not i.username in user_logins or

Check warning on line 459 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

test for membership should be 'not in'
i.digest is None]
# logger.debug("after clean up old users list is:\n%s" %
# '\n'.join(["%s" % i for i in conf['users']]))
Expand Down Expand Up @@ -460,7 +496,7 @@
# '\n'.join(["%s" % i for i in conf['users']]))


def refresh_user_creds(configuration, protocol, username):

Check failure on line 499 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

unused function 'refresh_user_creds' (60% confidence)
"""Reload user credentials for username if they changed on disk. That is,
add user entries in configuration.daemon_conf['users'] for all active keys
and passwords enabled in configuration. Optionally add short ID username
Expand Down Expand Up @@ -533,7 +569,7 @@
if conf['allow_digest']:
matches += [(proto_authdigests, authdigests_path)]
for (auth_file, path) in matches:
if not path in changed_paths:

Check warning on line 572 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

test for membership should be 'not in'
# logger.debug("Skipping %s without changes" % path)
continue
# Missing alias symlink - should be fixed for user instead
Expand Down Expand Up @@ -590,7 +626,7 @@
return (conf, changed_users)


def refresh_job_creds(configuration, protocol, username):

Check failure on line 629 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

unused function 'refresh_job_creds' (60% confidence)
"""Reload job credentials for username (SESSIONID) if they changed on disk.
That is, add user entries in configuration.daemon_conf['jobs'] for any
corresponding active job keys.
Expand All @@ -604,7 +640,7 @@
last_update = conf['time_stamp']
logger = conf.get("logger", logging.getLogger())
creds_lock = conf.get('creds_lock', None)
if not protocol in ('sftp',):

Check warning on line 643 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

test for membership should be 'not in'
logger.error("invalid protocol: %s" % protocol)
return (conf, changed_jobs)
if not possible_job_id(configuration, username):
Expand Down Expand Up @@ -681,7 +717,7 @@
return (conf, changed_jobs)


def refresh_share_creds(configuration, protocol, username,

Check failure on line 720 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

unused function 'refresh_share_creds' (60% confidence)
share_modes=['read-write']):
"""Reload sharelink credentials for username (SHARE_ID) if they changed on
disk. That is, add user entries in configuration.daemon_conf['shares'] for
Expand All @@ -698,10 +734,9 @@
base_dir = configuration.user_home.rstrip(os.sep) + os.sep
changed_shares = []
conf = configuration.daemon_conf
last_update = conf['time_stamp']
logger = conf.get("logger", logging.getLogger())
creds_lock = conf.get('creds_lock', None)
if not protocol in ('sftp', 'davs', 'ftps', ):

Check warning on line 739 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

test for membership should be 'not in'
logger.error("invalid protocol: %s" % protocol)
return (conf, changed_shares)
if [kind for kind in share_modes if kind != 'read-write']:
Expand All @@ -717,27 +752,44 @@
logger.error('refresh share creds called with invalid username %s: %s'
% (username, err))
mode = 'INVALID-SHARELINK'
if not mode in share_modes:

Check warning on line 755 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

test for membership should be 'not in'
logger.error("invalid share mode %s for %s" % (mode, username))
return (conf, changed_shares)

if protocol in ('ssh', 'sftp', 'scp', 'rsync'):
proto_authkeys = ssh_authkeys
proto_authpasswords = ssh_authpasswords
proto_authdigests = ssh_authdigests
elif protocol in ('dav', 'davs'):
proto_authkeys = davs_authkeys
proto_authpasswords = davs_authpasswords
proto_authdigests = davs_authdigests
elif protocol in ('ftp', 'ftps'):
proto_authkeys = ftps_authkeys
proto_authpasswords = ftps_authpasswords
proto_authdigests = ftps_authdigests
elif protocol in ('https', 'openid'):
private_auth_file = False
proto_authkeys = https_authkeys
proto_authpasswords = https_authpasswords
proto_authdigests = https_authdigests
else:
logger.error("invalid protocol: %s" % protocol)
logger.error("Invalid protocol: %s" % protocol)
return (conf, changed_shares)

# logger.debug("Updating share creds for %s" % username)
link_path = os.path.join(configuration.sharelink_home, mode, username)
# NOTE: accept any pub keys in standard authorized_keys location, too
# NOTE: accept any pub keys in standard authorized_keys location
authkeys_path = os.path.realpath(os.path.join(link_path, proto_authkeys))
# NOTE: accept password in standard authorized password/digest location
authpasswords_path = os.path.realpath(os.path.join(link_path,
proto_authpasswords))
authdigests_path = os.path.realpath(os.path.join(link_path,
proto_authdigests))
# logger.debug("Checking share creds changes for %s (%s)" %
# (username, authkeys_path))
changed_paths = get_share_changes(conf, username, link_path, authkeys_path)
changed_paths = get_share_changes(conf, username, link_path, authkeys_path,
authpasswords_path, authdigests_path)
if not changed_paths:
# logger.debug("No share creds changes for %s" % username)
return (conf, changed_shares)
Expand All @@ -761,13 +813,23 @@
# Otherwise it would choke on shares with trailing slash in path.
share_root = link_dest.replace(base_dir, '').strip(os.sep)
share_pw_hash, share_pw_digest = None, None
# NOTE: just use share_id as static password/digest for now
# NOTE: just use share_id as static password/digest if unset. Empty or
# invalid file disables password/digest login leaving only key.
if conf['allow_password']:
share_pw_hash = generate_password_hash(configuration, share_id)
if os.path.exists(authpasswords_path):
# Empty or invalid password file disables password login
if os.path.getsize(authpasswords_path) >= min_authpasswords_bytes:

Check warning on line 821 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

line too long (82 > 80 characters)
share_pw_hash = get_authpasswords(authpasswords_path)
else:
share_pw_hash = generate_password_hash(configuration, share_id)
if conf['allow_digest']:
share_pw_digest = generate_password_digest(
configuration, dav_domain, share_id, share_id,
configuration.site_digest_salt)
if os.path.exists(authdigests_path):
if os.path.getsize(authdigests_path) >= min_authdigests_bytes:
share_pw_digest = get_authpasswords(authdigests_path)
else:
share_pw_digest = generate_password_digest(
configuration, dav_domain, share_id, share_id,
configuration.site_digest_salt)
all_keys = []
if conf['allow_publickey'] and os.path.exists(authkeys_path):
all_keys = get_authkeys(authkeys_path)
Expand Down Expand Up @@ -822,7 +884,7 @@
return (conf, changed_shares)


def refresh_jupyter_creds(configuration, protocol, username):

Check failure on line 887 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

unused function 'refresh_jupyter_creds' (60% confidence)
"""Loads the active ssh keyset for username (SESSIONID).
The protocol argument specifies which auth files to use.
Returns a tuple with the updated daemon_conf and the list of changed
Expand Down Expand Up @@ -895,7 +957,7 @@
return (conf, active_jupyter_creds)


def update_login_map(daemon_conf, changed_users, changed_jobs=[],

Check failure on line 960 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

unused function 'update_login_map' (60% confidence)
changed_shares=[], changed_jupyter=[]):
"""Update internal login_map from contents of 'users', 'jobs' and
'shares' in daemon_conf. This is done considering Login objects matching
Expand Down Expand Up @@ -928,7 +990,7 @@
creds_lock.release()


def login_map_lookup(daemon_conf, username):

Check failure on line 993 in mig/shared/griddaemons/login.py

View workflow job for this annotation

GitHub Actions / Style check python and annotate

unused function 'login_map_lookup' (60% confidence)
"""Get creds associated with username in login_map in a thread-safe
fashion. Returns a list of credential objects, which is empty if username
is not found in login_map.
Expand Down