Warning
This document is for an in-development version of Galaxy. You can alternatively view this page in the latest release if it exists or view the top of the latest release's documentation.
Source code for galaxy.auth.providers.pam_auth
"""
Created on 13/07/2015
Author Peter van Heusden (pvh@sanbi.ac.za)
"""
import logging
import shlex
from galaxy.util import (
commands,
string_as_bool,
)
from . import AuthProvider
log = logging.getLogger(__name__)
"""
This module provides an AuthProvider for PAM (pluggable authentication module) authentication.
PAM is the Pluggable Authentication Module system (http://www.linux-pam.org/)
It relies on python-pam (https://pypi.python.org/pypi/python-pam)
Configuration is via config/auth_conf.xml and the following options are supported:
- auto-register: True/False: automatically register an account for an unknown user. Default: False
- maildomain: string: all valid users fall within the specified mail domain. Default: None
- login-use-email: True/False: Parse the email address to get login details. Default: False
- login-use-username: True/False: Use the username argument for login details. Default: False
Technical note: when a user is not found in the database,
their username is the user part of a user@host email
address. After user creation, however, the username is
the user's public name.
- pam-service: string: The service name to use for PAM authentication. Default: galaxy
- use-external-helper: True/False: Run an external helper script as root with sudo to do
authentication. If False authentication is done
by the module directly. Default: False
Technical note: some PAM modules (e.g. pam_unix.so)
require to be run as root to authenticate users.
- authentication-helper-script: string: Absolute path to helper script to run for authentication. Default: None
There needs to be a config (in /etc/sudoers or /etc/sudoers.d)
that allows the galaxy user to run this as root with no password check
For example:
galaxy ALL=(root) NOPASSWD: /opt/galaxy/scripts/auth/pam_auth_helper.py
Configuration example (for internal authentication, use email for user details):
<authenticator>
<type>PAM</type>
<options>
<auto-register>True</auto-register>
<maildomain>example.com</maildomain>
<login-use-email>True</login-use-email>
<pam-service>ssh</pam-service>
</options>
</authenticator>
"""
[docs]class PAM(AuthProvider):
plugin_type = "PAM"
[docs] def authenticate(self, email, username, password, options, request):
pam_username = None
auto_register_username = None
auto_register_email = None
force_fail = False
if not options["redact_username_in_logs"]:
log.debug(
f"use username: {options.get('login-use-username')} use email {options.get('login-use-email', False)} email {email} username {username}"
)
# check email based login first because if email exists in Galaxy DB
# we will be given the "public name" as username
if string_as_bool(options.get("login-use-email", False)) and email is not None:
if "@" in email:
(email_user, email_domain) = email.split("@")
pam_username = email_user
if email_domain == options.get("maildomain", None):
auto_register_email = email
if username is not None:
auto_register_username = username
else:
auto_register_username = email_user
else:
log.debug("PAM authenticate: warning: email does not match configured PAM maildomain")
# no need to fail: if auto-register is not enabled, this
# might still be a valid user
else:
log.debug("PAM authenticate: email must be used to login, but no valid email found")
force_fail = True
elif string_as_bool(options.get("login-use-username", False)):
# if we get here via authenticate_user then
# user will be "public name" and
# email address will be as per registered user
if username is not None:
pam_username = username
if email is not None:
auto_register_email = email
elif options.get("maildomain", None) is not None:
# we can register a user with this username and mail domain
# if auto registration is enabled
auto_register_email = f"{username}@{options['maildomain']}"
auto_register_username = username
else:
log.debug("PAM authenticate: username login selected but no username provided")
force_fail = True
else:
log.debug("PAM authenticate: could not find username for PAM")
force_fail = True
if force_fail:
return None, "", ""
pam_service = options.get("pam-service", "galaxy")
use_helper = string_as_bool(options.get("use-external-helper", False))
log.debug(f"PAM auth: will use external helper: {use_helper}")
authenticated = False
if use_helper:
authentication_helper = options.get("authentication-helper-script", "/bin/false").strip()
log.debug(f"PAM auth: external helper script: {authentication_helper}")
if not authentication_helper.startswith("/"):
# don't accept relative path
authenticated = False
else:
auth_cmd = shlex.split(f"/usr/bin/sudo -n {authentication_helper}")
log.debug(f"PAM auth: external helper cmd: {auth_cmd}")
message = f"{pam_service}\n{pam_username}\n{password}\n"
try:
output = commands.execute(auth_cmd, input=message)
except commands.CommandLineException as e:
if e.stderr != "":
log.debug(
f"PAM auth: external authentication script had errors: status {e.returncode} error {e.stderr}"
)
output = e.stdout
if output.strip() == "True":
authenticated = True
else:
authenticated = False
else:
try:
import pam
except ImportError:
log.debug("PAM authenticate: could not load pam module, PAM authentication disabled")
return None, "", ""
p_auth = pam.pam()
authenticated = p_auth.authenticate(pam_username, password, service=pam_service)
if authenticated:
log.debug(
f"PAM authentication successful for {'redacted' if options['redact_username_in_logs'] else pam_username}"
)
return True, auto_register_email, auto_register_username
else:
log.debug(
f"PAM authentication failed for {'redacted' if options['redact_username_in_logs'] else pam_username}"
)
return False, "", ""
[docs] def authenticate_user(self, user, password, options, request):
return self.authenticate(user.email, user.username, password, options, request)[0]
__all__ = ("PAM",)