Improve code quality

move doveadm call to sysutils
password hashers name and label are now class properties
This commit is contained in:
Anthony Bourguignon
2018-10-14 10:33:58 +02:00
parent b321228a82
commit 1836cee5fb
4 changed files with 68 additions and 46 deletions
+12 -22
View File
@@ -6,17 +6,13 @@ Password hashers for Modoboa.
from __future__ import unicode_literals
import os
from django.utils.encoding import force_text
from modoboa.core.password_hashers.advanced import ( # NOQA:F401
BLFCRYPTHasher, MD5CRYPTHasher, SHA256CRYPTHasher, SHA512CRYPTHasher
)
from modoboa.core.password_hashers.base import ( # NOQA:F401
CRYPTHasher, MD5Hasher, PLAINHasher, SHA256Hasher
)
from modoboa.lib.sysutils import exec_cmd
from modoboa.lib.sysutils import doveadm_cmd
def get_password_hasher(scheme):
@@ -35,25 +31,19 @@ def get_password_hasher(scheme):
hasher = PLAINHasher
return hasher
def get_dovecot_schemes():
"""Return schemes supported by dovecot"""
supported_schemes = ["{PLAIN}"]
dpath = None
code, output = exec_cmd("which doveadm")
if not code:
dpath = force_text(output).strip()
supported_schemes = None
try:
code, schemes = doveadm_cmd("pw -l")
except OSError:
pass
else:
known_paths = ("/usr/bin/doveadm", "/usr/local/bin/doveadm")
for fpath in known_paths:
if os.path.isfile(fpath) and os.access(fpath, os.X_OK):
dpath = fpath
if dpath:
try:
code, schemes = exec_cmd("%s pw -l" % dpath)
except OSError:
pass
else:
supported_schemes = ["{{{}}}".format(scheme)
for scheme in force_text(schemes).split()]
supported_schemes = ["{{{}}}".format(scheme)
for scheme in schemes.split()]
if not supported_schemes:
supported_schemes = ['{PLAIN}']
return supported_schemes
+1 -11
View File
@@ -21,8 +21,6 @@ class BLFCRYPTHasher(PasswordHasher):
Supports rounds and provides compatibility with dovecot on
*BSD systems.
"""
name = "blfcrypt"
label = "blfcrypt"
@property
def scheme(self):
@@ -46,14 +44,12 @@ class BLFCRYPTHasher(PasswordHasher):
class MD5CRYPTHasher(PasswordHasher):
"""
MD5-CRYPT password hasher.
This scheme can't be considered as secure anymore.
"""
name = "md5crypt"
label = "md5crypt"
_weak = True
@property
def scheme(self):
@@ -70,15 +66,12 @@ class MD5CRYPTHasher(PasswordHasher):
class SHA256CRYPTHasher(PasswordHasher):
"""
SHA256-CRYPT password hasher.
Supports rounds and is a good compromise between security and
performance.
"""
name = "sha256crypt"
label = "sha256crypt"
@property
def scheme(self):
@@ -97,15 +90,12 @@ class SHA256CRYPTHasher(PasswordHasher):
class SHA512CRYPTHasher(PasswordHasher):
"""
SHA512-CRYPT password hasher.
Supports rounds and is the strongest scheme provided by
Modoboa. Requires more resource than SHA256-CRYPT.
"""
name = "sha512crypt"
label = "sha512crypt"
@property
def scheme(self):
+15 -13
View File
@@ -19,14 +19,24 @@ from django.utils.encoding import force_bytes, force_text
class PasswordHasher(object):
"""
Base class of all hashers.
"""
_weak = False
def __init__(self, target="local"):
self._target = target
class __metaclass__(type):
"""class properties"""
@property
def name(cls):
return cls.__name__.rstrip('Hasher').lower()
@property
def label(cls):
return cls.name if not cls._weak else "{} (weak)".format(cls.name)
def _encrypt(self, clearvalue, salt=None):
raise NotImplementedError
@@ -75,12 +85,10 @@ class PasswordHasher(object):
class PLAINHasher(PasswordHasher):
"""
Plain (ie. clear) password hasher.
"""
name = "plain"
label = "plain (weak)"
_weak = True
@property
def scheme(self):
@@ -91,14 +99,12 @@ class PLAINHasher(PasswordHasher):
class CRYPTHasher(PasswordHasher):
"""
crypt password hasher.
Uses python `crypt` standard module.
"""
name = "crypt"
label = "crypt (weak)"
_weak = True
@property
def scheme(self):
@@ -117,14 +123,12 @@ class CRYPTHasher(PasswordHasher):
class MD5Hasher(PasswordHasher):
"""
MD5 password hasher.
Uses python `hashlib` standard module.
"""
name = "md5"
label = "md5 (weak)"
_weak = True
@property
def scheme(self):
@@ -136,14 +140,12 @@ class MD5Hasher(PasswordHasher):
class SHA256Hasher(PasswordHasher):
"""
SHA256 password hasher.
Uses python `hashlib` and `base64` standard modules.
"""
name = "sha256"
label = "sha256 (weak)"
_weak = True
@property
def scheme(self):
+40
View File
@@ -8,9 +8,13 @@ This module extra functions/shortcuts to communicate with the system
from __future__ import unicode_literals
import inspect
import os
import re
import subprocess
from django.conf import settings
from django.utils.encoding import force_text
def exec_cmd(cmd, sudo_user=None, pinput=None, capture_output=True, **kwargs):
"""Execute a shell command.
@@ -42,6 +46,42 @@ def exec_cmd(cmd, sudo_user=None, pinput=None, capture_output=True, **kwargs):
return process.returncode, output
def doveadm_cmd(params, sudo_user=None, pinput=None, capture_output=True, **kwargs):
"""Execute doveadm command.
Run doveadm command using the current user. Set :keyword:`sudo_user` if
you need different privileges.
:param str params: the parameters to give to doveadm
:param str sudo_user: a valid system username
:param str pinput: data to send to process's stdin
:param bool capture_output: capture process output or not
:rtype: tuple
:return: return code, command output
"""
dpath = None
code, output = exec_cmd("which doveadm")
if not code:
dpath = force_text(output).strip()
else:
known_paths = getattr(
settings, "DOVEADM_LOOKUP_PATH",
("/usr/bin/doveadm", "/usr/local/bin/doveadm")
)
for fpath in known_paths:
if os.path.isfile(fpath) and os.access(fpath, os.X_OK):
dpath = fpath
break
if dpath:
return exec_cmd("{} {}".format(dpath, params),
sudo_user=sudo_user,
pinput=pinput,
capture_output=capture_output,
**kwargs)
else:
raise OSError("doveadm command not found")
def guess_extension_name():
"""Tries to guess the application's name by inspecting the stack.