ispmailadmin là ứng dụng web quản lý ISP mailserver. Ưu điểm là mã nguồn mỡ, gọn nhẹ, đủ tính năng cơ bản. Với mailserver hỗ trợ nhiều domain, cần phân cấp quản lý cho từng domain, vì vậy phải sửa lại mã nguồn với mục đích:
- Mỗi domain có admin riêng
- Admin của domain này không nhìn thấy dữ liệu của domain khác
- Superadmin quản lý tất cả domain
- Các ứng dụng liên quan đều dùng cùng mã hóa mật khẩu (ARGON2ID)
- Can thiệp vào mã nguồn ít nhất có thể
Chúng ta cần sửa 5 file như sau:
1. cfg/config.inc.php
Thêm vào các lựa chọn mã hóa
- DEFAULT
- BCRYPT
- ARGON2I
- ARGON2ID
Trong đó định nghĩa sẵn IMA_CFG_MULTI_ADM là một mãng json chuyển từ mãng PHP có dạng
- sa => password
- domain1 => password1
- domain2 => password2
2. inc/defs.inc.php
Thêm kiểu IMA_LOGINTYPE_MULTI_ADM
3. inc/IspMailAdminApp.inc.php
Chỉnh sửa hàm mã hóa và cho phép đăng nhập nhiều admin
4. inc/Database_mysqli.inc.php
Chuyển truy cập các bảng sang các view. Mỗi domain khi được tạo sẽ có các view tương ứng để chỉ nhìn thấy dữ liệu của domain mình thôi
5. inc/EmailDomains.inc.php
- Không cho phép admin từng domain có thể thêm hay xóa domain
- Khi superadmin tạo/xóa domain thì tạo/xóa các view tương ứng
Script chỉnh sửa các file trên
F=$W/mailadmin/inc/defs.inc.php
[ -f "$F.org" ]&&cp -f "$F.org" "$F" || cp -f "$F" "$F.org"
sed -i "/ADMAUTO/ a\
define('IMA_LOGINTYPE_MULTI_ADM',4);
" $F
F=$W/mailadmin/cfg/config.inc.php
cp -f $W/mailadmin/cfg/config.sample.inc.php $F
sed -i "/\(IMA_CFG_ADM_USER\|IMA_CFG_ADM_PASS\)/s#\(.*\)#// \1#
s#^.*\(define.*IMA_CFG_DB_SOCKET'\).*#\1, '/run/mysqld/mysqld.sock');#
s/\(^.*'IMA_CFG_DB_USER'\).*/\1,'$RW_U');/
s/\(^.*'IMA_CFG_DB_PASSWORD'\).*/\1,'$RW_P');/
s/\(.*'IMA_CFG_DB_DATABASE'\).*/\1,'$DB');/
/IMA_LOGINTYPE_ACCOUNT/a\
define('IMA_CFG_LOGIN', IMA_LOGINTYPE_MULTI_ADM);\n\
define('IMA_CFG_MULTI_ADM','\{$cfg\}');
/IMA_CFG_USE_\(BCRYPT_HASHES\|SHA256_HASHES\|MD5_HASHES\)/d
/PASSWORD HASHES/{N;N;a\
define('CRYPT','PASSWORD_ARGON2ID');\n\
\/\/ CRYPT: DEFAULT, BCRYPT, ARGON2I, ARGON2ID\n\
ini_set('sendmail_path','/usr/sbin/sendmail -t -i');
}
" $F
F=$W/mailadmin/inc/IspMailAdminApp.inc.php
[ -f "$F.org" ]&&cp -f "$F.org" "$F"||cp -f "$F" "$F.org"
sed -i "s/(BLF-CRYPT|SHA256-CRYPT|PLAIN-MD5)/PASSWORD_\(DEFAULT|BCRYPT|ARGON2I|ARGON2ID\)/
/function makePwd_DbHash/{N;a\
return \"\{\".substr(CRYPT,9).\"\}\".password_hash(\$sPwdPlain, constant(CRYPT));
}
/\!defined('IMA_CFG_LOGIN'));/a\
elseif(IMA_CFG_LOGIN==IMA_LOGINTYPE_MULTI_ADM)\{\
foreach(json_decode(IMA_CFG_MULTI_ADM) as \$u=>\$p)\{\
if((\$this->aReqParam['sloginuser']==\$u)&&(\$this->aReqParam['sloginpass']==\$p))\{\
\$this->iIdUser=-1;\
\$this->sIdPage='page_welcome';\
\$_SESSION['domain']=\$u;\
\$bRetVal=true;\
break;\}\}\}
" $F
F=$W/mailadmin/inc/Database_mysqli.inc.php
[ -f "$F.org" ]&&cp -f "$F.org" "$F"||cp -f "$F" "$F.org"
sed -i "/function query\b/i\
private function dQuery(\$sQuery)\{return (\$_SESSION['domain']=='sa')?\$sQuery:preg_replace('\/\\\\bvirtual_\/',str_replace('.','_',\$_SESSION['domain']).'_virtual_',\$sQuery);\}
/\(function query\b\|function queryOneRow\b\)/{N;a\
\$sQuery=\$this->dQuery(\$sQuery);
}
" $F
F=$W/mailadmin/inc/EmailDomains.inc.php
[ -f "$F.org" ]&&cp -f "$F.org" "$F"||cp -f "$F" "$F.org"
sed -i "/'cmd_create':\|'cmd_delete':/a\
if(\$_SESSION['domain']!='sa')return false;
/create domain/{N;a\
elseif(0!=(\$iErr=\$this->createViews(\$sName)));
}
/No such Domain/{N;a\
elseif(0!=(\$iErr=\$this->dropViews(\$iId)));
}
\#METHOD PRIVATE#a\
private function dropViews(\$id)\{\
\$iErr=1;if(0!=\$this->App->DB->queryOneRow(\$r,\"SELECT name FROM virtual_domains WHERE id='\$id'\"));\
elseif(NULL===\$r);else\{\$name=\$r['name'];\$n=str_replace('.','_',\$name);\
if(0!=\$this->App->DB->state(\"DROP VIEW IF EXISTS \$\{n\}_virtual_domains,\$\{n\}_virtual_users,\$\{n\}_virtual_aliases\"));\
else\{\$a=json_decode(IMA_CFG_MULTI_ADM,true);unset(\$a[\$name]);\$F='cfg/config.inc.php';\
file_put_contents(\$F,preg_replace('/(\\\\'IMA_CFG_MULTI_ADM\\\\').+/',\"\$1,'\".json_encode(\$a).\"');\",file_get_contents(\$F)));\$iErr=0;\}\}\
unlink(\"INFO/\$name.README\");return (\$iErr);\}\n\
private function createViews(\$name)\{\
\$iErr=1;if(0!=\$this->App->DB->queryOneRow(\$r,\"SELECT id FROM virtual_domains WHERE name='\$name'\"));\
elseif(NULL===\$r);else\{\$id=\$r['id'];\$n=str_replace('.','_',\$name);\
if(0!=\$this->App->DB->state(\"CREATE OR REPLACE VIEW \$\{n\}_virtual_domains AS SELECT * FROM virtual_domains WHERE id=\$id\"));\
elseif(0!=\$this->App->DB->state(\"CREATE OR REPLACE VIEW \$\{n\}_virtual_users AS SELECT * FROM virtual_users WHERE domain_id='\$id'\"));\
elseif(0!=\$this->App->DB->state(\"CREATE OR REPLACE VIEW \$\{n\}_virtual_aliases AS SELECT * FROM virtual_aliases WHERE domain_id='\$id'\"));\
else\{\$p=uniqid();\n\
file_put_contents(\"INFO\/\$\{name\}.README\",str_replace(array('ydomain','ypassword'),array(\"\$name\",\"\$p\"),file_get_contents('INFO\/sample.README')));\
\$a=json_decode(IMA_CFG_MULTI_ADM,true);\$a[\$name]=\$p;\$F='cfg\/config.inc.php';\
\$p='\{'.substr(CRYPT,9).'\}'.password_hash(\$name,constant(CRYPT));\
if(false===file_put_contents(\$F,preg_replace('\/(\\\\'IMA_CFG_MULTI_ADM\\\\').+\/',\"\$1,'\".json_encode(\$a).\"');\",file_get_contents(\$F))));\
elseif(0!=\$this->App->DB->state(\"REPLACE INTO virtual_users (domain_id,email,password) VALUES ('\$id','postmaster@\$name','\$p')\"));\
elseif(false===mail(\"postmaster@\$name\",\"Informations of mailserver \$name\",file_get_contents(\"INFO\/\$\{name\}.README\"),\"From: postmater@\$D\"));\
else \$iErr=0;\}\}return(\$iErr);\}
" $F