handle and test a few unlikely cases when Spawners reuse tokens

- test that .will_resume preserves tokens (worked, but wasn't tested)

If a Spawner reuses a token, validate it in the db:

- verify that it's in the db
- if it doesn't map onto the right user, revoke the token
- if it's not in the db, insert it as a user-provided token

The most likely case is prior unclean shutdown of something like DockerSpawner,
where a spawn failed and thus the token was revoked,
but the container was in fact created.
This commit is contained in:
Min RK
2017-08-17 13:13:14 +02:00
parent ef7d6dc091
commit 512bbae5cb
2 changed files with 112 additions and 3 deletions

View File

@@ -385,12 +385,31 @@ class User(HasTraits):
# prior to 0.7, spawners had to store this info in user.server themselves.
# Handle < 0.7 behavior with a warning, assuming info was stored in db by the Spawner.
self.log.warning("DEPRECATION: Spawner.start should return (ip, port) in JupyterHub >= 0.7")
if spawner.api_token != api_token:
if spawner.api_token and spawner.api_token != api_token:
# Spawner re-used an API token, discard the unused api_token
orm_token = orm.APIToken.find(self.db, api_token)
if orm_token is not None:
self.db.delete(orm_token)
self.db.commit()
# check if the re-used API token is valid
found = orm.APIToken.find(self.db, spawner.api_token)
if found:
if found.user is not self.orm_user:
self.log.error("%s's server is using %s's token! Revoking this token.",
self.name, (found.user or found.service).name)
self.db.delete(found)
self.db.commit()
raise ValueError("Invalid token for %s!" % self.name)
else:
# Spawner.api_token has changed, but isn't in the db.
# What happened? Maybe something unclean in a resumed container.
self.log.warning("%s's server specified its own API token that's not in the database",
self.name
)
# use generated=False because we don't trust this token
# to have been generated properly
self.new_api_token(spawner.api_token, generated=False)
except Exception as e:
if isinstance(e, gen.TimeoutError):
self.log.warning("{user}'s server failed to start in {s} seconds, giving up".format(