add pre/post-spawn hooks for Authenticators

allows setup/cleanup to be performed by the authenticator

use this to open PAM sessions at spawn
and close them at stop,
rather than open at login and never close.
This commit is contained in:
Min RK
2015-10-14 16:07:12 +02:00
parent bd3c878c67
commit 6f2aefb990
2 changed files with 41 additions and 7 deletions

View File

@@ -58,6 +58,18 @@ class Authenticator(LoggingConfigurable):
and return None on failed authentication. and return None on failed authentication.
""" """
def pre_spawn_start(self, user, spawner):
"""Hook called before spawning a user's server.
Can be used to do auth-related startup, e.g. opening PAM sessions.
"""
def post_spawn_stop(self, user, spawner):
"""Hook called after stopping a user container.
Can be used to do auth-related cleanup, e.g. closing PAM sessions.
"""
def check_whitelist(self, user): def check_whitelist(self, user):
""" """
Return True if the whitelist is empty or user is in the whitelist. Return True if the whitelist is empty or user is in the whitelist.
@@ -210,9 +222,22 @@ class PAMAuthenticator(LocalAuthenticator):
return return
try: try:
pamela.authenticate(username, data['password'], service=self.service) pamela.authenticate(username, data['password'], service=self.service)
pamela.open_session(username, service=self.service)
except pamela.PAMError as e: except pamela.PAMError as e:
self.log.warn("PAM Authentication failed: %s", e) self.log.warn("PAM Authentication failed: %s", e)
else: else:
return username return username
def pre_spawn_start(self, user, spawner):
"""Open PAM session for user"""
try:
pamela.open_session(user.name, service=self.service)
except pamela.PAMError as e:
self.log.warn("Failed to open PAM session for %s: %s", user.name, e)
def post_spawn_stop(self, user, spawner):
"""Close PAM session for user"""
try:
pamela.close_session(user.name, service=self.service)
except pamela.PAMError as e:
self.log.warn("Failed to close PAM session for %s: %s", user.name, e)

View File

@@ -363,6 +363,9 @@ class User(Base):
spawner.clear_state() spawner.clear_state()
spawner.api_token = api_token spawner.api_token = api_token
# trigger pre-spawn hook on authenticator
if (authenticator):
yield gen.maybe_future(authenticator.pre_spawn_start(self, spawner))
self.spawn_pending = True self.spawn_pending = True
# wait for spawner.start to return # wait for spawner.start to return
try: try:
@@ -429,21 +432,27 @@ class User(Base):
and cleanup after it. and cleanup after it.
""" """
self.spawn_pending = False self.spawn_pending = False
if self.spawner is None: spawner = self.spawner
if spawner is None:
return return
self.spawner.stop_polling() spawner.stop_polling()
self.stop_pending = True self.stop_pending = True
try: try:
status = yield self.spawner.poll() status = yield spawner.poll()
if status is None: if status is None:
yield self.spawner.stop() yield self.spawner.stop()
self.spawner.clear_state() spawner.clear_state()
self.state = self.spawner.get_state() self.state = spawner.get_state()
self.server = None self.server = None
inspect(self).session.commit() inspect(self).session.commit()
finally: finally:
self.stop_pending = False self.stop_pending = False
# trigger post-spawner hook on authenticator
auth = spawner.authenticator
if auth:
yield gen.maybe_future(
auth.post_spawn_stop(self, spawner)
)
class APIToken(Base): class APIToken(Base):
"""An API token""" """An API token"""