mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2026-03-13 17:17:23 +01:00
split management daemon authorization from authentication and use 'doveadm pw' rather than 'doveadm auth test' so that it is decoupled from dovecot's login mechanism
This was done to pave the way for two-factor authentication, but that's still a ways off.
This commit is contained in:
@@ -3,7 +3,7 @@ import base64, os, os.path
|
||||
from flask import make_response
|
||||
|
||||
import utils
|
||||
from mailconfig import get_mail_user_privileges
|
||||
from mailconfig import get_mail_password, get_mail_user_privileges
|
||||
|
||||
DEFAULT_KEY_PATH = '/var/lib/mailinabox/api.key'
|
||||
DEFAULT_AUTH_REALM = 'Mail-in-a-Box Management Server'
|
||||
@@ -40,10 +40,11 @@ class KeyAuthService:
|
||||
with create_file_with_mode(self.key_path, 0o640) as key_file:
|
||||
key_file.write(self.key + '\n')
|
||||
|
||||
def is_authenticated(self, request, env):
|
||||
def authenticate(self, request, env):
|
||||
"""Test if the client key passed in HTTP Authorization header matches the service key
|
||||
or if the or username/password passed in the header matches an administrator user.
|
||||
Returns 'OK' if the key is good or the user is an administrator, otherwise an error message."""
|
||||
Returns a list of user privileges (e.g. [] or ['admin']) raise a ValueError on
|
||||
login failure."""
|
||||
|
||||
def decode(s):
|
||||
return base64.b64decode(s.encode('ascii')).decode('ascii')
|
||||
@@ -63,46 +64,56 @@ class KeyAuthService:
|
||||
|
||||
header = request.headers.get('Authorization')
|
||||
if not header:
|
||||
return "No authorization header provided."
|
||||
raise ValueError("No authorization header provided.")
|
||||
|
||||
username, password = parse_basic_auth(header)
|
||||
|
||||
if username in (None, ""):
|
||||
return "Authorization header invalid."
|
||||
raise ValueError("Authorization header invalid.")
|
||||
elif username == self.key:
|
||||
return "OK"
|
||||
# The user passed the API key which grants administrative privs.
|
||||
return ["admin"]
|
||||
else:
|
||||
return self.check_imap_login( username, password, env)
|
||||
# The user is trying to log in with a username and password.
|
||||
# Raises or returns privs.
|
||||
return self.get_user_credentials(username, password, env)
|
||||
|
||||
def check_imap_login(self, email, pw, env):
|
||||
# Validate a user's credentials.
|
||||
def get_user_credentials(self, email, pw, env):
|
||||
# Validate a user's credentials. On success returns a list of
|
||||
# privileges (e.g. [] or ['admin']). On failure raises a ValueError
|
||||
# with a login error message.
|
||||
|
||||
# Sanity check.
|
||||
if email == "" or pw == "":
|
||||
return "Enter an email address and password."
|
||||
raise ValueError("Enter an email address and password.")
|
||||
|
||||
# Get the hashed password of the user. Raise a ValueError if the
|
||||
# email address does not correspond to a user.
|
||||
pw_hash = get_mail_password(email, env)
|
||||
|
||||
# Authenticate.
|
||||
try:
|
||||
# Use doveadm to check credentials. doveadm will return
|
||||
# Use 'doveadm pw' to check credentials. doveadm will return
|
||||
# a non-zero exit status if the credentials are no good,
|
||||
# and check_call will raise an exception in that case.
|
||||
utils.shell('check_call', [
|
||||
"/usr/bin/doveadm",
|
||||
"auth", "test",
|
||||
email, pw
|
||||
"/usr/bin/doveadm", "pw",
|
||||
"-p", pw,
|
||||
"-t", pw_hash,
|
||||
])
|
||||
except:
|
||||
# Login failed.
|
||||
return "Invalid email address or password."
|
||||
raise ValueError("Invalid password.")
|
||||
|
||||
# Authorize.
|
||||
# (This call should never fail on a valid user.)
|
||||
# Get privileges for authorization.
|
||||
|
||||
# (This call should never fail on a valid user. But if it did fail, it would
|
||||
# return a tuple of an error message and an HTTP status code.)
|
||||
privs = get_mail_user_privileges(email, env)
|
||||
if isinstance(privs, tuple): raise Exception("Error getting privileges.")
|
||||
if "admin" not in privs:
|
||||
return "You are not an administrator for this system."
|
||||
|
||||
return "OK"
|
||||
# Return a list of privileges.
|
||||
return privs
|
||||
|
||||
def _generate_key(self):
|
||||
raw_key = os.urandom(32)
|
||||
|
||||
Reference in New Issue
Block a user