mirror of
https://github.com/YunoHost/moulinette.git
synced 2024-09-03 20:06:31 +02:00
Get rid of gnupg, just store a hash of the id:token to reauthenticate later using session info
This commit is contained in:
parent
3822496622
commit
7482f052f6
5 changed files with 60 additions and 60 deletions
|
@ -52,7 +52,6 @@ Requirements
|
||||||
|
|
||||||
* Python 2.7
|
* Python 2.7
|
||||||
* python-bottle (>= 0.10)
|
* python-bottle (>= 0.10)
|
||||||
* python-gnupg (>= 0.3)
|
|
||||||
* python-ldap (>= 2.4)
|
* python-ldap (>= 2.4)
|
||||||
* PyYAML
|
* PyYAML
|
||||||
|
|
||||||
|
|
1
debian/control
vendored
1
debian/control
vendored
|
@ -13,7 +13,6 @@ Depends: ${misc:Depends}, ${python:Depends},
|
||||||
python-ldap,
|
python-ldap,
|
||||||
python-yaml,
|
python-yaml,
|
||||||
python-bottle (>= 0.12),
|
python-bottle (>= 0.12),
|
||||||
python-gnupg,
|
|
||||||
python-gevent-websocket,
|
python-gevent-websocket,
|
||||||
python-argcomplete,
|
python-argcomplete,
|
||||||
python-toml,
|
python-toml,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
sphinx
|
sphinx
|
||||||
gnupg
|
|
||||||
mock
|
mock
|
||||||
pyyaml
|
pyyaml
|
||||||
toml
|
toml
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
"instance_already_running": "An instance is already running",
|
"instance_already_running": "An instance is already running",
|
||||||
"invalid_argument": "Invalid argument '{argument}': {error}",
|
"invalid_argument": "Invalid argument '{argument}': {error}",
|
||||||
"invalid_password": "Invalid password",
|
"invalid_password": "Invalid password",
|
||||||
|
"invalid_token": "Invalid token - please authenticate",
|
||||||
"invalid_usage": "Invalid usage, pass --help to see help",
|
"invalid_usage": "Invalid usage, pass --help to see help",
|
||||||
"ldap_attribute_already_exists": "Attribute '{attribute}' already exists with value '{value}'",
|
"ldap_attribute_already_exists": "Attribute '{attribute}' already exists with value '{value}'",
|
||||||
"ldap_operation_error": "An error occurred during LDAP operation",
|
"ldap_operation_error": "An error occurred during LDAP operation",
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import gnupg
|
|
||||||
import logging
|
import logging
|
||||||
|
import hashlib
|
||||||
|
|
||||||
from moulinette.cache import open_cachefile, get_cachedir
|
from moulinette.cache import open_cachefile, get_cachedir
|
||||||
from moulinette.core import MoulinetteError
|
from moulinette.core import MoulinetteError
|
||||||
|
@ -88,24 +88,11 @@ class BaseAuthenticator(object):
|
||||||
"""
|
"""
|
||||||
if self.is_authenticated:
|
if self.is_authenticated:
|
||||||
return self
|
return self
|
||||||
store_session = True if password and token else False
|
|
||||||
|
|
||||||
if token:
|
|
||||||
try:
|
|
||||||
# Extract id and actual token
|
|
||||||
s_id, s_token = token
|
|
||||||
except TypeError as e:
|
|
||||||
logger.error("unable to extract token parts from '%s' because '%s'", token, e)
|
|
||||||
if password is None:
|
|
||||||
raise MoulinetteError('error_see_log')
|
|
||||||
|
|
||||||
logger.info("session will not be stored")
|
|
||||||
store_session = False
|
|
||||||
else:
|
|
||||||
if password is None:
|
|
||||||
# Retrieve session
|
|
||||||
password = self._retrieve_session(s_id, s_token)
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Authenticate using the password
|
||||||
|
#
|
||||||
|
if password:
|
||||||
try:
|
try:
|
||||||
# Attempt to authenticate
|
# Attempt to authenticate
|
||||||
self.authenticate(password)
|
self.authenticate(password)
|
||||||
|
@ -116,10 +103,11 @@ class BaseAuthenticator(object):
|
||||||
self.name, self.vendor, e)
|
self.name, self.vendor, e)
|
||||||
raise MoulinetteError('unable_authenticate')
|
raise MoulinetteError('unable_authenticate')
|
||||||
|
|
||||||
# Store session
|
# Store session for later using the provided (new) token if any
|
||||||
if store_session:
|
if token:
|
||||||
try:
|
try:
|
||||||
self._store_session(s_id, s_token, password)
|
s_id, s_token = token
|
||||||
|
self._store_session(s_id, s_token)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
@ -127,6 +115,27 @@ class BaseAuthenticator(object):
|
||||||
else:
|
else:
|
||||||
logger.debug("session has been stored")
|
logger.debug("session has been stored")
|
||||||
|
|
||||||
|
#
|
||||||
|
# Authenticate using the token provided
|
||||||
|
#
|
||||||
|
elif token:
|
||||||
|
try:
|
||||||
|
s_id, s_token = token
|
||||||
|
# Attempt to authenticate
|
||||||
|
self._authenticate_session(s_id, s_token)
|
||||||
|
except MoulinetteError:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception("authentication (name: '%s', vendor: '%s') fails because '%s'",
|
||||||
|
self.name, self.vendor, e)
|
||||||
|
raise MoulinetteError('unable_authenticate')
|
||||||
|
|
||||||
|
#
|
||||||
|
# No credentials given, can't authenticate
|
||||||
|
#
|
||||||
|
else:
|
||||||
|
raise MoulinetteError('unable_authenticate')
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
# Private methods
|
# Private methods
|
||||||
|
@ -136,36 +145,31 @@ class BaseAuthenticator(object):
|
||||||
return open_cachefile('%s.asc' % session_id, mode,
|
return open_cachefile('%s.asc' % session_id, mode,
|
||||||
subdir='session/%s' % self.name)
|
subdir='session/%s' % self.name)
|
||||||
|
|
||||||
def _store_session(self, session_id, session_token, password):
|
def _store_session(self, session_id, session_token):
|
||||||
"""Store a session and its associated password"""
|
"""Store a session to be able to use it later to reauthenticate"""
|
||||||
gpg = gnupg.GPG()
|
|
||||||
gpg.encoding = 'utf-8'
|
|
||||||
|
|
||||||
# Encrypt the password using the session token
|
|
||||||
s = str(gpg.encrypt(password, None, symmetric=True, passphrase=session_token))
|
|
||||||
assert len(s), "For some reason GPG can't perform encryption, maybe check /root/.gnupg/gpg.conf or re-run with gpg = gnupg.GPG(verbose=True) ?"
|
|
||||||
|
|
||||||
|
# We store a hash of the session_id and the session_token (the token is assumed to be secret)
|
||||||
|
to_hash = "{id}:{token}".format(id=session_id, token=session_token)
|
||||||
|
hash_ = hashlib.sha256sum(to_hash).hexdigest()
|
||||||
with self._open_sessionfile(session_id, 'w') as f:
|
with self._open_sessionfile(session_id, 'w') as f:
|
||||||
f.write(s)
|
f.write(hash_)
|
||||||
|
|
||||||
def _retrieve_session(self, session_id, session_token):
|
def _authenticate_session(self, session_id, session_token):
|
||||||
"""Retrieve a session and return its associated password"""
|
"""Retrieve a session and return its associated password"""
|
||||||
try:
|
try:
|
||||||
with self._open_sessionfile(session_id, 'r') as f:
|
with self._open_sessionfile(session_id, 'r') as f:
|
||||||
enc_pwd = f.read()
|
stored_hash = f.read()
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
logger.debug("unable to retrieve session", exc_info=1)
|
logger.debug("unable to retrieve session", exc_info=1)
|
||||||
raise MoulinetteError('unable_retrieve_session', exception=e)
|
raise MoulinetteError('unable_retrieve_session', exception=e)
|
||||||
else:
|
else:
|
||||||
gpg = gnupg.GPG()
|
to_hash = "{id}:{token}".format(id=session_id, token=session_token)
|
||||||
gpg.encoding = 'utf-8'
|
hash_ = hashlib.sha256sum(to_hash).hexdigest()
|
||||||
|
|
||||||
decrypted = gpg.decrypt(enc_pwd, passphrase=session_token)
|
if hash_ != stored_hash:
|
||||||
if decrypted.ok is not True:
|
raise MoulinetteError('invalid_token')
|
||||||
error_message = "unable to decrypt password for the session: %s" % decrypted.status
|
else:
|
||||||
logger.error(error_message)
|
return
|
||||||
raise MoulinetteError('unable_retrieve_session', exception=error_message)
|
|
||||||
return decrypted.data
|
|
||||||
|
|
||||||
def _clean_session(self, session_id):
|
def _clean_session(self, session_id):
|
||||||
"""Clean a session cache
|
"""Clean a session cache
|
||||||
|
@ -181,5 +185,3 @@ class BaseAuthenticator(object):
|
||||||
os.remove(os.path.join(sessiondir, self.name, '%s.asc' % session_id))
|
os.remove(os.path.join(sessiondir, self.name, '%s.asc' % session_id))
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue