2014-08-17 22:43:57 +00:00
< h2 > Users< / h2 >
< style >
2014-10-07 20:24:11 +00:00
#user_table tr.account_inactive td.address { color: #888; text-decoration: line-through; }
2014-09-21 17:06:38 +00:00
#user_table .actions { margin-top: .33em; font-size: 95%; }
#user_table .account_inactive .if_active { display: none; }
#user_table .account_active .if_inactive { display: none; }
2015-03-22 13:59:05 +00:00
#user_table .account_active.if_inactive { display: none; }
2019-02-01 13:36:27 +00:00
.row-center { text-align: center; }
2014-08-17 22:43:57 +00:00
< / style >
< h3 > Add a mail user< / h3 >
internationalized domain names (DNS, web, CSRs, normalize to Unicode in database, prohibit non-ASCII characters in user account names)
* For non-ASCII domain names, we will keep the Unicode encoding in our users/aliases table. This is nice for the user and also simplifies things like sorting domain names (using Unicode lexicographic order is good, using ASCII lexicogrpahic order on IDNA is confusing).
* Write nsd config, nsd zone files, nginx config, and SSL CSRs with domains in IDNA-encoded ASCII.
* When checking SSL certificates, treat the CN and SANs as IDNA.
* Since Chrome has an interesting feature of converting Unicode to IDNA in <input type="email"> form fields, we'll also forcibly convert IDNA to Unicode in the domain part of email addresses before saving email addresses in the users/aliases tables so that the table is normalized to Unicode.
* Don't allow non-ASCII characters in user account email addresses. Dovecot gets confused when querying the Sqlite database (which we observed even for non-word ASCII characters too, so it may not be related to the character encoding).
2015-01-17 13:41:53 +00:00
< p > Add an email address to this system. This will create a new login username/password.< / p >
2014-08-17 22:43:57 +00:00
< form class = "form-inline" role = "form" onsubmit = "return do_add_user(); return false;" >
< div class = "form-group" >
< label class = "sr-only" for = "adduserEmail" > Email address< / label >
< input type = "email" class = "form-control" id = "adduserEmail" placeholder = "Email Address" >
< / div >
< div class = "form-group" >
< label class = "sr-only" for = "adduserPassword" > Password< / label >
< input type = "password" class = "form-control" id = "adduserPassword" placeholder = "Password" >
< / div >
< div class = "form-group" >
< select class = "form-control" id = "adduserPrivs" >
< option value = "" > Normal User< / option >
< option value = "admin" > Administrator< / option >
< / select >
< / div >
2019-01-31 20:57:04 +00:00
< div class = "form-group" >
< label class = "sr-only" for = "adduserQuota" > Quota< / label >
2019-02-11 14:10:09 +00:00
< input type = "text" class = "form-control" id = "adduserQuota" placeholder = "Quota" style = "width:5em;" >
2019-01-31 20:57:04 +00:00
< / div >
2014-08-17 22:43:57 +00:00
< button type = "submit" class = "btn btn-primary" > Add User< / button >
< / form >
internationalized domain names (DNS, web, CSRs, normalize to Unicode in database, prohibit non-ASCII characters in user account names)
* For non-ASCII domain names, we will keep the Unicode encoding in our users/aliases table. This is nice for the user and also simplifies things like sorting domain names (using Unicode lexicographic order is good, using ASCII lexicogrpahic order on IDNA is confusing).
* Write nsd config, nsd zone files, nginx config, and SSL CSRs with domains in IDNA-encoded ASCII.
* When checking SSL certificates, treat the CN and SANs as IDNA.
* Since Chrome has an interesting feature of converting Unicode to IDNA in <input type="email"> form fields, we'll also forcibly convert IDNA to Unicode in the domain part of email addresses before saving email addresses in the users/aliases tables so that the table is normalized to Unicode.
* Don't allow non-ASCII characters in user account email addresses. Dovecot gets confused when querying the Sqlite database (which we observed even for non-word ASCII characters too, so it may not be related to the character encoding).
2015-01-17 13:41:53 +00:00
< ul style = "margin-top: 1em; padding-left: 1.5em; font-size: 90%;" >
2021-01-06 02:12:01 +00:00
< li > Passwords must be at least eight characters consisting of English letters and numbers only. For best results, < a href = "#" onclick = "return generate_random_password()" > generate a random password< / a > .< / li >
2023-05-13 10:49:34 +00:00
< li > Use < a href = "#aliases" > aliases< / a > to create email addresses that forward to existing accounts.< / li >
internationalized domain names (DNS, web, CSRs, normalize to Unicode in database, prohibit non-ASCII characters in user account names)
* For non-ASCII domain names, we will keep the Unicode encoding in our users/aliases table. This is nice for the user and also simplifies things like sorting domain names (using Unicode lexicographic order is good, using ASCII lexicogrpahic order on IDNA is confusing).
* Write nsd config, nsd zone files, nginx config, and SSL CSRs with domains in IDNA-encoded ASCII.
* When checking SSL certificates, treat the CN and SANs as IDNA.
* Since Chrome has an interesting feature of converting Unicode to IDNA in <input type="email"> form fields, we'll also forcibly convert IDNA to Unicode in the domain part of email addresses before saving email addresses in the users/aliases tables so that the table is normalized to Unicode.
* Don't allow non-ASCII characters in user account email addresses. Dovecot gets confused when querying the Sqlite database (which we observed even for non-word ASCII characters too, so it may not be related to the character encoding).
2015-01-17 13:41:53 +00:00
< li > Administrators get access to this control panel.< / li >
2023-05-13 10:49:34 +00:00
< li > User accounts cannot contain any international (non-ASCII) characters, but < a href = "#aliases" > aliases< / a > can.< / li >
2019-02-06 12:47:30 +00:00
< li > Quotas may not contain any spaces, commas or decimal points. Suffixes of G (gigabytes) and M (megabytes) are allowed. For unlimited storage enter 0 (zero)< / li >
internationalized domain names (DNS, web, CSRs, normalize to Unicode in database, prohibit non-ASCII characters in user account names)
* For non-ASCII domain names, we will keep the Unicode encoding in our users/aliases table. This is nice for the user and also simplifies things like sorting domain names (using Unicode lexicographic order is good, using ASCII lexicogrpahic order on IDNA is confusing).
* Write nsd config, nsd zone files, nginx config, and SSL CSRs with domains in IDNA-encoded ASCII.
* When checking SSL certificates, treat the CN and SANs as IDNA.
* Since Chrome has an interesting feature of converting Unicode to IDNA in <input type="email"> form fields, we'll also forcibly convert IDNA to Unicode in the domain part of email addresses before saving email addresses in the users/aliases tables so that the table is normalized to Unicode.
* Don't allow non-ASCII characters in user account email addresses. Dovecot gets confused when querying the Sqlite database (which we observed even for non-word ASCII characters too, so it may not be related to the character encoding).
2015-01-17 13:41:53 +00:00
< / ul >
2014-08-17 22:43:57 +00:00
< h3 > Existing mail users< / h3 >
< table id = "user_table" class = "table" style = "width: auto" >
2014-10-07 20:24:11 +00:00
< thead >
< tr >
2019-01-28 21:27:03 +00:00
< th width = "35%" > Email Address< / th >
2019-02-01 13:36:27 +00:00
< th class = "row-center" > Messages< / th >
< th class = "row-center" > Size< / th >
< th class = "row-center" > Used< / th >
< th class = "row-center" > Quota< / th >
2014-10-07 20:24:11 +00:00
< th > Actions< / th >
< / tr >
< / thead >
2014-08-17 22:43:57 +00:00
< tbody >
< / tbody >
< / table >
< div style = "display: none" >
< table >
< tr id = "user-template" >
2014-10-07 20:24:11 +00:00
< td class = 'address' >
< / td >
2019-02-01 13:36:27 +00:00
< td class = "box-count row-center" > < / td >
< td class = "box-size row-center" > < / td >
< td class = "percent row-center" > < / td >
< td class = "quota row-center" >
2019-01-28 14:19:36 +00:00
< / td >
2014-10-07 20:24:11 +00:00
< td class = 'actions' >
2014-09-21 17:06:38 +00:00
< span class = 'privs' >
< / span >
2019-10-10 14:54:31 +00:00
< span class = "if_active" >
< a href = "#" onclick = "users_set_quota(this); return false;" class = 'setquota' title = "Set Quota" >
set quota
< / a >
|
< / span >
2014-09-21 17:24:01 +00:00
< span class = "if_active" >
< a href = "#" onclick = "users_set_password(this); return false;" class = 'setpw' title = "Set Password" >
set password
< / a >
|
< / span >
2014-09-21 17:06:38 +00:00
< span class = 'add-privs' >
< / span >
< a href = "#" onclick = "users_remove(this); return false;" class = 'if_active' title = "Archive Account" >
archive account
< / a >
2014-10-07 20:24:11 +00:00
< / td >
< / tr >
2015-03-22 13:59:05 +00:00
< tr id = "user-extra-template" class = "if_inactive" >
< td colspan = "3" style = "border: 0; padding-top: 0" >
< div class = 'restore_info' style = 'color: #888; font-size: 90%' > To restore account, create a new account with this email address. Or to permanently delete the mailbox, delete the directory < tt > < / tt > on the machine.< / div >
2014-08-17 22:43:57 +00:00
< / td >
< / tr >
< / table >
< / div >
2016-08-08 11:28:10 +00:00
< h3 > Mail user API (advanced)< / h3 >
2016-06-29 11:35:42 +00:00
2016-08-08 11:28:10 +00:00
< p > Use your box’ s mail user API to add/change/remove users from the command-line or custom services you build.< / p >
2016-06-29 11:35:42 +00:00
< p > Usage:< / p >
2016-08-08 11:28:10 +00:00
< pre > curl -X < b > VERB< / b > [-d "< b > parameters< / b > "] --user {email}:{password} https://{{hostname}}/admin/mail/users[< b > action< / b > ]< / pre >
2016-06-29 11:35:42 +00:00
2016-08-08 11:28:10 +00:00
< p > Brackets denote an optional argument. Please note that the POST body < code > parameters< / code > must be URL-encoded.< / p >
2016-06-29 11:35:42 +00:00
2016-08-08 11:28:10 +00:00
< p > The email and password given to the < code > --user< / code > option must be an administrative user on this system.< / p >
2016-06-29 11:35:42 +00:00
2016-08-08 11:28:10 +00:00
< h4 style = "margin-bottom: 0" > Verbs< / h4 >
< table class = "table" style = "margin-top: .5em" >
2016-06-29 11:35:42 +00:00
< thead > < th > Verb< / th > < th > Action< / th > < th > < / th > < / thead >
2016-08-08 11:28:10 +00:00
< tr > < td > GET< / td > < td > < i > (none)< / i > < / td > < td > Returns a list of existing mail users. Adding < code > ?format=json< / code > to the URL will give JSON-encoded results.< / td > < / tr >
2019-10-04 15:32:50 +00:00
< tr >
< td > POST< / td >
< td > /add< / td >
< td > Adds a new mail user. Required POST-body parameters are < code > email< / code > and < code > password< / code > . Optional parameters: < code > privilege=admin< / code > and < code > quota< / code > < / td >
< / tr >
2020-05-17 16:17:44 +00:00
< tr >
< td > POST< / td >
< td > /remove< / td >
< td > Removes a mail user. Required POST-by parameter is < code > email< / code > .< / td >
< / tr >
2016-08-08 11:28:10 +00:00
< tr > < td > POST< / td > < td > /privileges/add< / td > < td > Used to make a mail user an admin. Required POST-body parameters are < code > email< / code > and < code > privilege=admin< / code > .< / td > < / tr >
< tr > < td > POST< / td > < td > /privileges/remove< / td > < td > Used to remove the admin privilege from a mail user. Required POST-body parameter is < code > email< / code > .< / td > < / tr >
2019-10-04 15:32:50 +00:00
< tr >
< td > GET< / td >
< td > /quota< / td >
< td > Get the quota for a mail user. Required POST-body parameters are < code > email< / code > and will return JSON result< / td >
< / tr >
< tr >
< td > POST< / td >
< td > /quota< / td >
< td > Set the quota for a mail user. Required POST-body parameters are < code > email< / code > and < code > quota< / code > .< / td >
< / tr >
2016-06-29 11:35:42 +00:00
< / table >
< h4 > Examples:< / h4 >
2016-08-08 11:28:10 +00:00
< p > Try these examples. For simplicity the examples omit the < code > --user me@mydomain.com:yourpassword< / code > command line argument which you must fill in with your administrative email address and password.< / p >
2016-06-29 11:35:42 +00:00
2016-08-08 11:28:10 +00:00
< pre > # Gives a JSON-encoded list of all mail users
2016-06-29 11:35:42 +00:00
curl -X GET https://{{hostname}}/admin/mail/users?format=json
2016-08-08 11:28:10 +00:00
# Adds a new email user
2016-06-29 11:35:42 +00:00
curl -X POST -d "email=new_user@mydomail.com" -d "password=s3curE_pa5Sw0rD" https://{{hostname}}/admin/mail/users/add
2016-08-08 11:28:10 +00:00
# Removes a email user
2016-06-29 11:35:42 +00:00
curl -X POST -d "email=new_user@mydomail.com" https://{{hostname}}/admin/mail/users/remove
2016-08-08 11:28:10 +00:00
# Adds admin privilege to an email user
2016-06-29 11:35:42 +00:00
curl -X POST -d "email=new_user@mydomail.com" -d "privilege=admin" https://{{hostname}}/admin/mail/users/privileges/add
2016-08-08 11:28:10 +00:00
# Removes admin privilege from an email user
2016-06-29 11:35:42 +00:00
curl -X POST -d "email=new_user@mydomail.com" https://{{hostname}}/admin/mail/users/privileges/remove
< / pre >
2014-08-17 22:43:57 +00:00
< script >
function show_users() {
2019-01-31 23:21:44 +00:00
api(
"/system/default-quota",
"GET",
{},
function(r) {
$('#adduserQuota').val(r['default-quota']);
}
);
2014-08-17 22:43:57 +00:00
$('#user_table tbody').html("< tr > < td colspan = '2' class = 'text-muted' > Loading...< / td > < / tr > ")
api(
"/mail/users",
"GET",
{ format: 'json' },
function(r) {
$('#user_table tbody').html("");
for (var i = 0; i < r.length ; i + + ) {
2021-05-04 11:09:04 +00:00
var hdr = $("< tr > < th colspan = '6' style = 'background-color: #EEE' > < / th > < / tr > ");
2021-02-28 14:35:40 +00:00
hdr.find('th').text(r[i].domain);
2014-10-07 19:28:07 +00:00
$('#user_table tbody').append(hdr);
2014-08-17 22:43:57 +00:00
2014-10-07 19:28:07 +00:00
for (var k = 0; k < r [ i ] . users . length ; k + + ) {
var user = r[i].users[k];
2014-09-03 10:17:46 +00:00
2014-10-07 19:28:07 +00:00
var n = $("#user-template").clone();
2014-10-07 20:24:11 +00:00
var n2 = $("#user-extra-template").clone();
2014-10-07 19:28:07 +00:00
n.attr('id', '');
2014-10-07 20:24:11 +00:00
n2.attr('id', '');
$('#user_table tbody').append(n);
$('#user_table tbody').append(n2);
2014-08-17 22:43:57 +00:00
2014-10-07 19:28:07 +00:00
n.addClass("account_" + user.status);
2014-10-07 20:24:11 +00:00
n2.addClass("account_" + user.status);
2014-10-07 19:28:07 +00:00
n.attr('data-email', user.email);
2019-01-28 21:27:03 +00:00
n.attr('data-quota', user.quota);
2019-01-28 14:19:36 +00:00
n.find('.address').text(user.email);
2019-02-04 23:48:38 +00:00
n.find('.box-count').text((user.box_count).toLocaleString('en'));
2019-02-01 13:36:27 +00:00
if (user.box_count == '?') {
n.find('.box-count').attr('title', 'Message count is unkown')
}
2019-01-29 17:18:48 +00:00
n.find('.box-size').text(user.box_size);
2019-02-01 13:36:27 +00:00
if (user.box_size == '?') {
n.find('.box-size').attr('title', 'Mailbox size is unkown')
}
2019-02-04 23:48:38 +00:00
n.find('.percent').text(user.percent);
2019-01-28 21:27:03 +00:00
n.find('.quota').text((user.quota == '0') ? 'unlimited' : user.quota);
2014-10-07 20:24:11 +00:00
n2.find('.restore_info tt').text(user.mailbox);
2014-08-17 22:43:57 +00:00
2014-10-07 19:28:07 +00:00
if (user.status == 'inactive') continue;
2014-08-17 22:43:57 +00:00
2014-10-07 19:28:07 +00:00
var add_privs = ["admin"];
for (var j = 0; j < user.privileges.length ; j + + ) {
var p = $("< span > < b > < span class = 'name' > < / span > < / b > (< a href = '#' onclick = 'mod_priv(this, \"remove\"); return false;' title = 'Remove Privilege' > remove privilege< / a > ) |< / span > ");
p.find('span.name').text(user.privileges[j]);
n.find('.privs').append(p);
if (add_privs.indexOf(user.privileges[j]) >= 0)
add_privs.splice(add_privs.indexOf(user.privileges[j]), 1);
}
for (var j = 0; j < add_privs.length ; j + + ) {
var p = $("< span > < a href = '#' onclick = 'mod_priv(this, \"add\"); return false;' title = 'Add Privilege' > make < span class = 'name' > < / span > < / a > | < / span > ");
p.find('span.name').text(add_privs[j]);
n.find('.add-privs').append(p);
}
2014-08-17 22:43:57 +00:00
}
}
})
}
function do_add_user() {
var email = $("#adduserEmail").val();
var pw = $("#adduserPassword").val();
var privs = $("#adduserPrivs").val();
2019-01-31 20:57:04 +00:00
var quota = $("#adduserQuota").val();
2014-08-17 22:43:57 +00:00
api(
"/mail/users/add",
"POST",
{
email: email,
password: pw,
2019-01-31 20:57:04 +00:00
privileges: privs,
quota: quota
2014-08-17 22:43:57 +00:00
},
function(r) {
// Responses are multiple lines of pre-formatted text.
show_modal_error("Add User", $("< pre / > ").text(r));
show_users()
},
function(r) {
show_modal_error("Add User", r);
});
return false;
}
2014-09-21 17:24:01 +00:00
function users_set_password(elem) {
var email = $(elem).parents('tr').attr('data-email');
2015-06-06 12:33:31 +00:00
var yourpw = "";
2021-08-22 20:40:07 +00:00
if (api_credentials != null & & email == api_credentials.username)
2015-06-06 12:33:31 +00:00
yourpw = "< p class = 'text-danger' > If you change your own password, you will be logged out of this control panel and will need to log in again.< / p > ";
2014-09-21 17:24:01 +00:00
show_modal_confirm(
2016-05-17 23:46:10 +00:00
"Set Password",
2018-02-03 22:49:11 +00:00
$("< p > Set a new password for < b > " + email + "< / b > ?< / p > < p > < label for = 'users_set_password_pw' style = 'display: block; font-weight: normal' > New Password:< / label > < input type = 'password' id = 'users_set_password_pw' > < / p > < p > < small > Passwords must be at least eight characters and may not contain spaces.< / small > " + yourpw + "< / p > "),
2014-09-21 17:24:01 +00:00
"Set Password",
function() {
api(
"/mail/users/password",
"POST",
{
email: email,
password: $('#users_set_password_pw').val()
},
function(r) {
// Responses are multiple lines of pre-formatted text.
show_modal_error("Set Password", $("< pre / > ").text(r));
},
function(r) {
show_modal_error("Set Password", r);
});
});
}
2019-01-28 21:27:03 +00:00
function users_set_quota(elem) {
var email = $(elem).parents('tr').attr('data-email');
var quota = $(elem).parents('tr').attr('data-quota');
show_modal_confirm(
"Set Quota",
$("< p > Set quota for < b > " + email + "< / b > ?< / p > " +
"< p > " +
"< label for = 'users_set_quota' style = 'display: block; font-weight: normal' > Quota:< / label > " +
"< input type = 'text' id = 'users_set_quota' value = '" + quota + "' > < / p > " +
2019-01-31 23:35:55 +00:00
"< p > < small > Quotas may not contain any spaces or commas. Suffixes of G (gigabytes) and M (megabytes) are allowed.< / small > < / p > " +
2019-01-28 21:27:03 +00:00
"< p > < small > For unlimited storage enter 0 (zero)< / small > < / p > "),
"Set Quota",
function() {
api(
"/mail/users/quota",
"POST",
{
email: email,
quota: $('#users_set_quota').val()
},
function(r) {
show_users();
},
function(r) {
show_modal_error("Set Quota", r);
});
});
}
2014-08-17 22:43:57 +00:00
function users_remove(elem) {
var email = $(elem).parents('tr').attr('data-email');
2015-04-28 11:14:35 +00:00
// can't remove yourself
2021-08-22 20:40:07 +00:00
if (api_credentials != null & & email == api_credentials.username) {
2015-04-28 11:14:35 +00:00
show_modal_error("Archive User", "You cannot archive your own account.");
return;
}
2014-08-17 22:43:57 +00:00
show_modal_confirm(
"Archive User",
2014-09-21 17:24:01 +00:00
$("< p > Are you sure you want to archive < b > " + email + "< / b > ?< / p > < p > The user's mailboxes will not be deleted (you can do that later), but the user will no longer be able to log into any services on this machine.< / p > "),
2014-08-17 22:43:57 +00:00
"Archive",
function() {
api(
"/mail/users/remove",
"POST",
{
email: email
},
function(r) {
// Responses are multiple lines of pre-formatted text.
show_modal_error("Remove User", $("< pre / > ").text(r));
show_users();
},
function(r) {
show_modal_error("Remove User", r);
});
});
}
function mod_priv(elem, add_remove) {
var email = $(elem).parents('tr').attr('data-email');
var priv = $(elem).parents('td').find('.name').text();
// can't remove your own admin access
2021-08-22 20:40:07 +00:00
if (priv == "admin" & & add_remove == "remove" & & api_credentials != null & & email == api_credentials.username) {
2014-08-17 22:43:57 +00:00
show_modal_error("Modify Privileges", "You cannot remove the admin privilege from yourself.");
return;
}
var add_remove1 = add_remove.charAt(0).toUpperCase() + add_remove.substring(1);
show_modal_confirm(
"Modify Privileges",
2015-04-19 13:08:53 +00:00
$("< p > Are you sure you want to " + add_remove + " the " + priv + " privilege for < b > " + email + "< / b > ?< / p > "),
2014-08-17 22:43:57 +00:00
add_remove1,
function() {
api(
"/mail/users/privileges/" + add_remove,
"POST",
{
email: email,
privilege: priv
},
function(r) {
show_users();
});
});
}
2015-09-04 22:12:07 +00:00
function generate_random_password() {
var pw = "";
var charset = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789"; // confusable characters skipped
2017-02-14 19:24:59 +00:00
for (var i = 0; i < 12 ; i + + )
2015-09-04 22:12:07 +00:00
pw += charset.charAt(Math.floor(Math.random() * charset.length));
2019-01-31 22:56:25 +00:00
show_modal_error("Random Password", "< p > Here, try this:< / p > < p > < code style = 'font-size: 110%' > " + pw + "< / code > < / p > ");
2015-09-04 22:12:07 +00:00
return false; // cancel click
}
2014-09-03 10:17:46 +00:00
< / script >