mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2026-03-05 15:57:23 +01:00
Merge remote-tracking branch 'fspoettel/admin-panel-2fa' into totp
# Conflicts: # management/auth.py # management/daemon.py # management/mailconfig.py # setup/mail-users.sh
This commit is contained in:
@@ -575,7 +575,7 @@ apply_access_control() {
|
||||
# service accounts (except management):
|
||||
# can bind but not change passwords, including their own
|
||||
# can read all attributes of all users but not userPassword,
|
||||
# totpSecret, or totpMruToken
|
||||
# totpSecret, totpMruToken, or totpLabel
|
||||
# can read config subtree (permitted-senders, domains)
|
||||
# no access to services subtree, except their own dn
|
||||
# management service account:
|
||||
@@ -584,8 +584,8 @@ apply_access_control() {
|
||||
# users:
|
||||
# can bind and change their own password
|
||||
# can read and change their own shadowLastChange
|
||||
# cannot read or modify totpSecret, totpMruToken
|
||||
# can read attributess of other users except mailaccess, totpSecret, totpMruToken
|
||||
# cannot read or modify totpSecret, totpMruToken, totpLabel
|
||||
# can read attributess of other users except mailaccess, totpSecret, totpMruToken, totpLabel
|
||||
# no access to config subtree
|
||||
# no access to services subtree
|
||||
#
|
||||
@@ -607,7 +607,7 @@ olcAccess: to attrs=userPassword
|
||||
by self =wx
|
||||
by anonymous auth
|
||||
by * none
|
||||
olcAccess: to attrs=totpSecret,totpMruToken
|
||||
olcAccess: to attrs=totpSecret,totpMruToken,totpLabel
|
||||
by dn.exact="cn=management,${LDAP_SERVICES_BASE}" write
|
||||
by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read
|
||||
by * none
|
||||
|
||||
@@ -17,7 +17,6 @@ source setup/functions.sh # load our functions
|
||||
source /etc/mailinabox.conf # load global vars
|
||||
source ${STORAGE_ROOT}/ldap/miab_ldap.conf # user-data specific vars
|
||||
|
||||
|
||||
# ### User Authentication
|
||||
|
||||
# Have Dovecot query our database, and not system users, for authentication.
|
||||
|
||||
@@ -183,9 +183,11 @@ def migration_12(env):
|
||||
conn.close()
|
||||
|
||||
def migration_13(env):
|
||||
# Add a table for `totp_credentials`
|
||||
# Add the "mfa" table for configuring MFA for login to the control panel.
|
||||
db = os.path.join(env["STORAGE_ROOT"], 'mail/users.sqlite')
|
||||
shell("check_call", ["sqlite3", db, "CREATE TABLE IF NOT EXISTS totp_credentials (id INTEGER PRIMARY KEY AUTOINCREMENT, user_email TEXT NOT NULL UNIQUE, secret TEXT NOT NULL, mru_token TEXT, FOREIGN KEY (user_email) REFERENCES users(email) ON DELETE CASCADE);"])
|
||||
shell("check_call", ["sqlite3", db, "CREATE TABLE mfa (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL UNIQUE, type TEXT NOT NULL, secret TEXT NOT NULL, mru_token TEXT, label TEXT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE);"])
|
||||
|
||||
###########################################################
|
||||
|
||||
|
||||
def migration_miabldap_1(env):
|
||||
@@ -352,7 +354,8 @@ def run_miabldap_migrations():
|
||||
print(e)
|
||||
print()
|
||||
print("Your system may be in an inconsistent state now. We're terribly sorry. A re-install from a backup might be the best way to continue.")
|
||||
sys.exit(1)
|
||||
#sys.exit(1)
|
||||
raise e
|
||||
|
||||
ourver = next_ver
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import uuid, os, sqlite3, ldap3, hashlib
|
||||
|
||||
|
||||
def add_user(env, ldapconn, search_base, users_base, domains_base, email, password, privs, totp_secret, totp_mru_token, cn=None):
|
||||
def add_user(env, ldapconn, search_base, users_base, domains_base, email, password, privs, totp, cn=None):
|
||||
# Add a sqlite user to ldap
|
||||
# env are the environment variables
|
||||
# ldapconn is the bound ldap connection
|
||||
@@ -18,8 +18,7 @@ def add_user(env, ldapconn, search_base, users_base, domains_base, email, passwo
|
||||
# email is the user's email
|
||||
# password is the user's current sqlite password hash
|
||||
# privs is an array of privilege names for the user
|
||||
# totp_secret is the TOTP secret or None
|
||||
# totp_mru_token is the TOP most recently used token or None
|
||||
# totp contains the list of secrets, mru tokens, and labels
|
||||
# cn is the user's common name [optional]
|
||||
#
|
||||
# the email address should be as-is from sqlite (encoded as
|
||||
@@ -77,11 +76,11 @@ def add_user(env, ldapconn, search_base, users_base, domains_base, email, passwo
|
||||
attrs["sn"] = cn[cn.find(' ')+1:]
|
||||
|
||||
# add TOTP, if enabled
|
||||
if totp_secret:
|
||||
if totp:
|
||||
objectClasses.append('totpUser')
|
||||
attrs['totpSecret'] = totp_secret
|
||||
if totp_mru_token:
|
||||
attrs['totpMruToken'] = totp_mru_token
|
||||
attrs['totpSecret'] = totp["secret"]
|
||||
attrs['totpMruToken'] = totp["mru_token"]
|
||||
attrs['totpLabel'] = totp["label"]
|
||||
|
||||
# Add user
|
||||
dn = "uid=%s,%s" % (uid, users_base)
|
||||
@@ -105,23 +104,33 @@ def create_users(env, conn, ldapconn, ldap_base, ldap_users_base, ldap_domains_b
|
||||
# iterate through sqlite 'users' table and create each user in
|
||||
# ldap. returns a map of email->dn
|
||||
|
||||
try:
|
||||
c = conn.cursor()
|
||||
c.execute("select users.email, users.password, users.privileges, totp_credentials.secret, totp_credentials.mru_token from users left join totp_credentials on users.email = totp_credentials.user_email")
|
||||
|
||||
except:
|
||||
# old version of miab
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT email, password, privileges, NULL as secret, NULL as mru_token from users")
|
||||
# select users
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT id, email, password, privileges from users")
|
||||
|
||||
users = {}
|
||||
for row in c:
|
||||
email=row[0]
|
||||
password=row[1]
|
||||
privs=row[2]
|
||||
totp_secret=row[3]
|
||||
totp_mru_token=row[4]
|
||||
dn = add_user(env, ldapconn, ldap_base, ldap_users_base, ldap_domains_base, email, password, privs.split("\n"), totp_secret, totp_mru_token)
|
||||
user_id=row[0]
|
||||
email=row[1]
|
||||
password=row[2]
|
||||
privs=row[3]
|
||||
totp = None
|
||||
|
||||
c2 = conn.cursor()
|
||||
c2.execute("SELECT secret, mru_token, label from mfa where user_id=? and type='totp'", (user_id,));
|
||||
rowidx = 0
|
||||
for row2 in c2:
|
||||
if totp is None:
|
||||
totp = {
|
||||
"secret": [],
|
||||
"mru_token": [],
|
||||
"label": []
|
||||
}
|
||||
totp["secret"].append("{%s}%s" % (rowidx, row2[0]))
|
||||
totp["mru_token"].append("{%s}%s" % (rowidx, row2[1] or ''))
|
||||
totp["label"].append("{%s}%s" % (rowidx, row2[2] or ''))
|
||||
|
||||
dn = add_user(env, ldapconn, ldap_base, ldap_users_base, ldap_domains_base, email, password, privs.split("\n"), totp)
|
||||
users[email] = dn
|
||||
return users
|
||||
|
||||
|
||||
Reference in New Issue
Block a user