Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#635] client_init free functions can now suppress overwriting old .irodsA #663

Merged
merged 3 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,9 @@ Note, in the `pam_password` case, this involves sending the cleartext password
to the server (SSL should thus be enabled!) and then writing the scrambled token that
returns from the transaction.

If an .irodsA file exists already, it will be overwritten.
If an .irodsA file exists already, it will be overwritten by default; however, if these functions'
overwrite parameter is set to `False`, an exception of type `irods.client_init.irodsA_already_exists`
will be raised to indicate the older .irodsA file is present.

Examples:
For the `native` authentication scheme, we can use the currently set iRODS password to create .irodsA file from Python thus:
Expand Down
61 changes: 46 additions & 15 deletions irods/client_init.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,65 @@
import contextlib
import getpass
import os
import sys

from irods import (env_filename_from_keyword_args, derived_auth_filename)
import irods.client_configuration as cfg
import irods.password_obfuscation as obf
import irods.helpers as h
import getpass
import os
import sys

def write_native_credentials_to_secrets_file(password, **kw):
env_file = env_filename_from_keyword_args(kw)
auth_file = derived_auth_filename(env_file)
old_mask = None
@contextlib.contextmanager
def _open_file_for_protected_contents(file_path, *arg, **kw):
f = old_mask = None
try:
old_mask = os.umask(0o77)
open(auth_file,'w').write(obf.encode(password))
f = open(file_path, *arg, **kw)
yield f
finally:
if old_mask is not None:
os.umask(old_mask)

return True
if f is not None:
f.close()

class irodsA_already_exists(Exception):
pass

def _write_encoded_auth_value(auth_file, encode_input, overwrite):
if not auth_file:
raise RuntimeError(f'Path to irodsA ({auth_file}) is null.')
if not overwrite and os.path.exists(auth_file):
raise irodsA_already_exists(f'Overwriting not enabled and {auth_file} already exists.')
with _open_file_for_protected_contents(auth_file, 'w') as irodsA:
irodsA.write(obf.encode(encode_input))

def write_native_credentials_to_secrets_file(password, overwrite = True, **kw):
"""Write the credentials to an .irodsA file that will enable logging in with native authentication
using the given cleartext password.

If overwrite is False, irodsA_already_exists will be raised if an .irodsA is found at the
expected path.
"""
env_file = env_filename_from_keyword_args(kw)
auth_file = derived_auth_filename(env_file)
_write_encoded_auth_value(auth_file, password, overwrite)

def write_pam_credentials_to_secrets_file(password, overwrite = True, **kw):
"""Write the credentials to an .irodsA file that will enable logging in with PAM authentication
using the given cleartext password.

def write_pam_credentials_to_secrets_file( password ,**kw):
If overwrite is False, irodsA_already_exists will be raised if an .irodsA is found at the
expected path.
"""
s = h.make_session()
s.pool.account.password = password
to_encode = []
with cfg.loadlines( [dict(setting='legacy_auth.pam.password_for_auto_renew',value=None),
dict(setting='legacy_auth.pam.store_password_to_environment',value=False)] ):
to_encode = s.pam_pw_negotiated
if to_encode:
open(s.pool.account.derived_auth_file,'w').write(obf.encode(to_encode[0]))
return True
return False
if not to_encode:
raise RuntimeError(f'Password token was not passed from server.')
auth_file = s.pool.account.derived_auth_file
_write_encoded_auth_value(auth_file, to_encode[0], overwrite)

if __name__ == '__main__':
vector = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env bats
#
# Test creation of .irodsA for iRODS native authentication using the free function,
# irods.client_init.write_pam_credentials_to_secrets_file
# irods.client_init.write_native_credentials_to_secrets_file

. "$BATS_TEST_DIRNAME"/test_support_functions
PYTHON=python3
Expand All @@ -10,26 +10,18 @@ PYTHON=python3
# Run as ubuntu user with sudo; python_irodsclient must be installed (in either ~/.local or a virtualenv)
#

ALICES_OLD_PAM_PASSWD="test123"
ALICES_NEW_PAM_PASSWD="new_pass"
@test create_irods_secrets_file {

setup()
{
setup_pam_login_for_alice "$ALICES_OLD_PAM_PASSWD"
}

teardown()
{
finalize_pam_login_for_alice
test_specific_cleanup
}

@test create_secrets_file {

# Old .irodsA is already created, so we delete it and alter the pam password.
sudo chpasswd <<<"alice:$ALICES_NEW_PAM_PASSWD"
rm -f ~/.irods/.irodsA
$PYTHON -c "import irods.client_init; irods.client_init.write_pam_credentials_to_secrets_file('$ALICES_NEW_PAM_PASSWD')"
rm -fr ~/.irods
mkdir ~/.irods
cat > ~/.irods/irods_environment.json <<-EOF
{ "irods_host":"$(hostname)",
"irods_port":1247,
"irods_user_name":"rods",
"irods_zone_name":"tempZone"
}
EOF
$PYTHON -c "import irods.client_init; irods.client_init.write_native_credentials_to_secrets_file('rods')"

# Define the core Python to be run, basically a minimal code block ensuring that we can authenticate to iRODS
# without an exception being raised.
Expand All @@ -42,6 +34,5 @@ print ('env_auth_scheme=%s' % ses.pool.account._original_authentication_scheme)
"
OUTPUT=$($PYTHON -c "$SCRIPT")
# Assert passing value
[ $OUTPUT = "env_auth_scheme=pam_password" ]

[ $OUTPUT = "env_auth_scheme=native" ]
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env bats
#
# Test creation of .irodsA for iRODS pam_password authentication using the free function,
# irods.client_init.write_native_credentials_to_secrets_file
# irods.client_init.write_pam_credentials_to_secrets_file

. "$BATS_TEST_DIRNAME"/test_support_functions
PYTHON=python3
Expand All @@ -10,18 +10,26 @@ PYTHON=python3
# Run as ubuntu user with sudo; python_irodsclient must be installed (in either ~/.local or a virtualenv)
#

@test create_irods_secrets_file {
ALICES_OLD_PAM_PASSWD="test123"
ALICES_NEW_PAM_PASSWD="new_pass"

rm -fr ~/.irods
mkdir ~/.irods
cat > ~/.irods/irods_environment.json <<-EOF
{ "irods_host":"$(hostname)",
"irods_port":1247,
"irods_user_name":"rods",
"irods_zone_name":"tempZone"
}
EOF
$PYTHON -c "import irods.client_init; irods.client_init.write_native_credentials_to_secrets_file('rods')"
setup()
{
setup_pam_login_for_alice "$ALICES_OLD_PAM_PASSWD"
}

teardown()
{
finalize_pam_login_for_alice
test_specific_cleanup
}

@test create_secrets_file {

# Old .irodsA is already created, so we delete it and alter the pam password.
sudo chpasswd <<<"alice:$ALICES_NEW_PAM_PASSWD"
rm -f ~/.irods/.irodsA
$PYTHON -c "import irods.client_init; irods.client_init.write_pam_credentials_to_secrets_file('$ALICES_NEW_PAM_PASSWD')"

# Define the core Python to be run, basically a minimal code block ensuring that we can authenticate to iRODS
# without an exception being raised.
Expand All @@ -34,5 +42,6 @@ print ('env_auth_scheme=%s' % ses.pool.account._original_authentication_scheme)
"
OUTPUT=$($PYTHON -c "$SCRIPT")
# Assert passing value
[ $OUTPUT = "env_auth_scheme=native" ]
[[ $OUTPUT = "env_auth_scheme=pam"* ]]

}