com.aoindustries.aoserv.master.LinuxAccountHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aoserv-master Show documentation
Show all versions of aoserv-master Show documentation
Master server for the AOServ Platform.
/*
* aoserv-master - Master server for the AOServ Platform.
* Copyright (C) 2001-2013, 2015, 2016, 2017, 2018, 2019, 2020 AO Industries, Inc.
* [email protected]
* 7262 Bull Pen Cir
* Mobile, AL 36695
*
* This file is part of aoserv-master.
*
* aoserv-master is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* aoserv-master is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with aoserv-master. If not, see .
*/
package com.aoindustries.aoserv.master;
import com.aoindustries.aoserv.client.account.Account;
import com.aoindustries.aoserv.client.distribution.OperatingSystemVersion;
import com.aoindustries.aoserv.client.email.SpamAssassinMode;
import com.aoindustries.aoserv.client.linux.Group;
import com.aoindustries.aoserv.client.linux.GroupUser;
import com.aoindustries.aoserv.client.linux.PosixPath;
import com.aoindustries.aoserv.client.linux.Shell;
import com.aoindustries.aoserv.client.linux.User;
import com.aoindustries.aoserv.client.linux.User.Gecos;
import com.aoindustries.aoserv.client.linux.UserServer;
import com.aoindustries.aoserv.client.linux.UserType;
import com.aoindustries.aoserv.client.master.Permission;
import com.aoindustries.aoserv.client.password.PasswordChecker;
import com.aoindustries.aoserv.client.schema.AoservProtocol;
import com.aoindustries.aoserv.client.schema.Table;
import com.aoindustries.aoserv.client.web.Site;
import com.aoindustries.aoserv.client.web.tomcat.SharedTomcat;
import com.aoindustries.aoserv.daemon.client.AOServDaemonConnector;
import com.aoindustries.collections.IntList;
import com.aoindustries.dbc.DatabaseConnection;
import com.aoindustries.util.InternUtils;
import com.aoindustries.util.Tuple2;
import com.aoindustries.validation.ValidationException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* The LinuxAccountHandler
handles all the accesses to the Linux tables.
*
* @author AO Industries, Inc.
*/
final public class LinuxAccountHandler {
private LinuxAccountHandler() {
}
/** Matches value in /etc/login.defs on CentOS 7 */
private static final int
CENTOS_7_SYS_GID_MIN = 201,
CENTOS_7_SYS_UID_MIN = 201
;
/** Default sudo setting for newly added "aoadmin" system users. */
private static final String AOADMIN_SUDO = "ALL=(ALL) NOPASSWD: ALL";
/** Default sudo setting for newly added "aoserv-xen-migration" system users. */
private static final String AOSERV_XEN_MIGRATION_SUDO = "ALL=(ALL) NOPASSWD: /usr/sbin/xl -t migrate-receive";
private final static Map disabledUsers=new HashMap<>();
private final static Map disabledUserServers=new HashMap<>();
public static void checkAccessUser(DatabaseConnection conn, RequestSource source, String action, com.aoindustries.aoserv.client.linux.User.Name user) throws IOException, SQLException {
com.aoindustries.aoserv.client.master.User mu = MasterServer.getUser(conn, source.getCurrentAdministrator());
if(mu!=null) {
if(MasterServer.getUserHosts(conn, source.getCurrentAdministrator()).length!=0) {
IntList lsas = getUserServersForUser(conn, user);
boolean found = false;
for(Integer lsa : lsas) {
if(NetHostHandler.canAccessHost(conn, source, getServerForUserServer(conn, lsa))) {
found=true;
break;
}
}
if(!found) {
String message=
"currentAdministrator="
+source.getCurrentAdministrator()
+" is not allowed to access linux_account: action='"
+action
+", username="
+user
;
throw new SQLException(message);
}
}
} else {
AccountUserHandler.checkAccessUser(conn, source, action, user);
}
}
public static void checkAccessGroup(DatabaseConnection conn, RequestSource source, String action, Group.Name group) throws IOException, SQLException {
com.aoindustries.aoserv.client.master.User mu = MasterServer.getUser(conn, source.getCurrentAdministrator());
if(mu!=null) {
if(MasterServer.getUserHosts(conn, source.getCurrentAdministrator()).length!=0) {
IntList lsgs = getGroupServersForGroup(conn, group);
boolean found = false;
for(int lsg : lsgs) {
if(NetHostHandler.canAccessHost(conn, source, getServerForGroupServer(conn, lsg))) {
found=true;
break;
}
}
if(!found) {
String message=
"currentAdministrator="
+source.getCurrentAdministrator()
+" is not allowed to access linux_group: action='"
+action
+", name="
+group
;
throw new SQLException(message);
}
}
} else {
PackageHandler.checkAccessPackage(conn, source, action, getPackageForGroup(conn, group));
}
}
public static void checkAccessGroupUser(DatabaseConnection conn, RequestSource source, String action, int groupUser) throws IOException, SQLException {
checkAccessUser(conn, source, action, getUserForGroupUser(conn, groupUser));
checkAccessGroup(conn, source, action, getGroupForGroupUser(conn, groupUser));
}
public static boolean canAccessUserServer(DatabaseConnection conn, RequestSource source, int userServer) throws IOException, SQLException {
com.aoindustries.aoserv.client.master.User mu = MasterServer.getUser(conn, source.getCurrentAdministrator());
if(mu!=null) {
if(MasterServer.getUserHosts(conn, source.getCurrentAdministrator()).length!=0) {
return NetHostHandler.canAccessHost(conn, source, getServerForUserServer(conn, userServer));
} else return true;
} else {
return AccountUserHandler.canAccessUser(conn, source, getUserForUserServer(conn, userServer));
}
}
public static void checkAccessUserServer(DatabaseConnection conn, RequestSource source, String action, int userServer) throws IOException, SQLException {
if(!canAccessUserServer(conn, source, userServer)) {
String message=
"currentAdministrator="
+source.getCurrentAdministrator()
+" is not allowed to access linux_server_account: action='"
+action
+", id="
+userServer
;
throw new SQLException(message);
}
}
public static boolean canAccessGroupServer(DatabaseConnection conn, RequestSource source, int groupServer) throws IOException, SQLException {
return
PackageHandler.canAccessPackage(conn, source, getPackageForGroupServer(conn, groupServer))
&& NetHostHandler.canAccessHost(conn, source, getServerForGroupServer(conn, groupServer))
;
}
public static void checkAccessGroupServer(DatabaseConnection conn, RequestSource source, String action, int groupServer) throws IOException, SQLException {
if(!canAccessGroupServer(conn, source, groupServer)) {
String message=
"currentAdministrator="
+source.getCurrentAdministrator()
+" is not allowed to access linux_server_group: action='"
+action
+", id="
+groupServer
;
throw new SQLException(message);
}
}
/**
* Adds a linux account.
*/
public static void addUser(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
com.aoindustries.aoserv.client.linux.User.Name user,
Group.Name primaryGroup,
Gecos name,
Gecos office_location,
Gecos office_phone,
Gecos home_phone,
String type,
PosixPath shell,
boolean skipSecurityChecks
) throws IOException, SQLException {
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to add User named '"+User.MAIL+'\'');
// Make sure the shell is allowed for the type of account being added
if(!UserType.isAllowedShell(type, shell)) throw new SQLException("shell='"+shell+"' not allowed for type='"+type+'\'');
if(!skipSecurityChecks) {
AccountUserHandler.checkAccessUser(conn, source, "addUser", user);
if(AccountUserHandler.isUserDisabled(conn, user)) throw new SQLException("Unable to add User, Username disabled: "+user);
}
conn.executeUpdate(
"insert into linux.\"User\" values(?,?,?,?,?,?,?,now(),null)",
user,
name,
office_location,
office_phone,
home_phone,
type,
shell
);
// Notify all clients of the update
invalidateList.addTable(conn,
Table.TableID.LINUX_ACCOUNTS,
AccountUserHandler.getAccountForUser(conn, user),
InvalidateList.allHosts,
false
);
addGroupUser(
conn,
source,
invalidateList,
primaryGroup,
user,
true,
skipSecurityChecks
);
}
public static void addGroup(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
Group.Name name,
Account.Name packageName,
String type,
boolean skipSecurityChecks
) throws IOException, SQLException {
if(!skipSecurityChecks) {
PackageHandler.checkAccessPackage(conn, source, "addGroup", packageName);
if(PackageHandler.isPackageDisabled(conn, packageName)) throw new SQLException("Unable to add Group, Package disabled: "+packageName);
}
if (
name.equals(Group.FTPONLY)
|| name.equals(Group.MAIL)
|| name.equals(Group.MAILONLY)
) throw new SQLException("Not allowed to add Group: "+name);
conn.executeUpdate("insert into linux.\"Group\" values(?,?,?)", name, packageName, type);
// Notify all clients of the update
invalidateList.addTable(conn,
Table.TableID.LINUX_GROUPS,
PackageHandler.getAccountForPackage(conn, packageName),
InvalidateList.allHosts,
false
);
}
public static int addGroupUser(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
Group.Name group,
com.aoindustries.aoserv.client.linux.User.Name user,
boolean isPrimary,
boolean skipSecurityChecks
) throws IOException, SQLException {
if(group.equals(Group.MAIL)) throw new SQLException("Not allowed to add GroupUser for group '"+Group.MAIL+'\'');
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to add GroupUser for user '"+User.MAIL+'\'');
if(!skipSecurityChecks) {
if(
!group.equals(Group.FTPONLY)
&& !group.equals(Group.MAILONLY)
) checkAccessGroup(conn, source, "addGroupUser", group);
checkAccessUser(conn, source, "addGroupUser", user);
if(isUserDisabled(conn, user)) throw new SQLException("Unable to add GroupUser, User disabled: "+user);
}
if(group.equals(Group.FTPONLY)) {
// Only allowed to have ftponly group when it is a ftponly account
String type=getTypeForUser(conn, user);
if(!type.equals(UserType.FTPONLY)) throw new SQLException("Not allowed to add GroupUser for group '"+Group.FTPONLY+"' on non-ftp-only-type User named "+user);
}
if(group.equals(Group.MAILONLY)) {
// Only allowed to have mail group when it is a "mailonly" account
String type=getTypeForUser(conn, user);
if(!type.equals(UserType.EMAIL)) throw new SQLException("Not allowed to add GroupUser for group '"+Group.MAILONLY+"' on non-email-type User named "+user);
}
// Do not allow more than 31 groups per account
int count=conn.executeIntQuery("select count(*) from linux.\"GroupUser\" where \"user\"=?", user);
if(count >= GroupUser.MAX_GROUPS) throw new SQLException("Only "+GroupUser.MAX_GROUPS+" groups are allowed per user, username="+user+" already has access to "+count+" groups");
int groupUser = conn.executeIntUpdate(
"INSERT INTO linux.\"GroupUser\" VALUES (default,?,?,?,null) RETURNING id",
group,
user,
isPrimary
);
// Notify all clients of the update
invalidateList.addTable(
conn,
Table.TableID.LINUX_GROUP_ACCOUNTS,
InvalidateList.getAccountCollection(
AccountUserHandler.getAccountForUser(conn, user),
getAccountForGroup(conn, group)
),
getServersForGroupUser(conn, groupUser),
false
);
return groupUser;
}
public static int addUserServer(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
com.aoindustries.aoserv.client.linux.User.Name user,
int linuxServer,
PosixPath home,
boolean skipSecurityChecks
) throws IOException, SQLException {
if(user.equals(User.MAIL)) {
throw new SQLException("Not allowed to add UserServer for user '"+User.MAIL+'\'');
}
if(!skipSecurityChecks) {
checkAccessUser(conn, source, "addUserServer", user);
if(isUserDisabled(conn, user)) throw new SQLException("Unable to add UserServer, User disabled: "+user);
NetHostHandler.checkAccessHost(conn, source, "addUserServer", linuxServer);
AccountUserHandler.checkUserAccessHost(conn, source, "addUserServer", user, linuxServer);
}
// OperatingSystem settings
int osv = NetHostHandler.getOperatingSystemVersionForHost(conn, linuxServer);
if(osv == -1) throw new SQLException("Operating system version not known for server #" + linuxServer);
PosixPath httpdSharedTomcatsDir = OperatingSystemVersion.getHttpdSharedTomcatsDirectory(osv);
PosixPath httpdSitesDir = OperatingSystemVersion.getHttpdSitesDirectory(osv);
if(home.equals(UserServer.getDefaultHomeDirectory(user))) {
// Make sure no conflicting /home/u/username account exists.
String prefix = home + "/";
List conflicting = conn.executeStringListQuery(
"select distinct home from linux.\"UserServer\" where ao_server=? and substring(home from 1 for " + prefix.length() + ")=? order by home",
linuxServer,
prefix
);
if(!conflicting.isEmpty()) throw new SQLException("Found conflicting home directories: " + conflicting);
} else if(home.equals(UserServer.getHashedHomeDirectory(user))) {
// Make sure no conflicting /home/u account exists.
String conflictHome = "/home/" + user.toString().charAt(0);
if(
conn.executeBooleanQuery(
"select (select id from linux.\"UserServer\" where ao_server=? and home=? limit 1) is not null",
linuxServer,
conflictHome
)
) {
throw new SQLException("Found conflicting home directory: " + conflictHome);
}
} else {
String homeStr = home.toString();
// Must be in /www/... or /wwwgroup/... (or newer CentOS 7 equivalent of /var/www and /var/opt/apache-tomcat)
if(
!homeStr.startsWith(httpdSitesDir + "/")
&& !homeStr.startsWith(httpdSharedTomcatsDir + "/")
) throw new SQLException("Invalid home directory: " + home);
final String SLASH_WEBAPPS = "/webapps";
if(homeStr.startsWith(httpdSitesDir + "/")) {
// May also be in /www/(sitename)/webapps
String siteName = homeStr.substring(httpdSitesDir.toString().length() + 1);
if(siteName.endsWith(SLASH_WEBAPPS)) {
siteName = siteName.substring(0, siteName.length() - SLASH_WEBAPPS.length());
}
// May be in /www/(sitename)
int httpdSite = WebHandler.getSite(conn, linuxServer, siteName);
if(httpdSite != -1) {
if(!skipSecurityChecks) {
// Must be able to access an existing site
WebHandler.checkAccessSite(conn, source, "addUserServer", httpdSite);
}
} else {
// Must be a valid site name
if(!Site.isValidSiteName(siteName)) {
throw new SQLException("Invalid site name for www home directory: " + home);
}
}
}
if(homeStr.startsWith(httpdSharedTomcatsDir + "/")) {
// May also be in /wwwgroup/(tomcatname)/webapps
String tomcatName = homeStr.substring(httpdSharedTomcatsDir.toString().length() + 1);
if(tomcatName.endsWith(SLASH_WEBAPPS)) {
tomcatName = tomcatName.substring(0, tomcatName.length() - SLASH_WEBAPPS.length());
}
// May be in /wwwgroup/(tomcatname)
int httpdSharedTomcat = WebHandler.getSharedTomcat(conn, linuxServer, tomcatName);
if(httpdSharedTomcat != -1) {
if(!skipSecurityChecks) {
// Must be able to access an existing site
WebHandler.checkAccessSharedTomcat(conn, source, "addUserServer", httpdSharedTomcat);
}
} else {
// Must be a valid tomcat name
if(!SharedTomcat.isValidSharedTomcatName(tomcatName)) {
throw new SQLException("Invalid shared tomcat name for wwwgroup home directory: " + home);
}
}
}
}
// The primary group for this user must exist on this server
Group.Name primaryGroup=getPrimaryGroup(conn, user, osv);
int primaryLSG=getGroupServer(conn, primaryGroup, linuxServer);
if(primaryLSG<0) throw new SQLException("Unable to find primary Linux group '"+primaryGroup+"' on Server #"+linuxServer+" for Linux account '"+user+"'");
// Now allocating unique to entire system for server portability between farms
//String farm=ServerHandler.getFarmForServer(conn, linuxServer);
int userServer = conn.executeIntUpdate(
"INSERT INTO\n"
+ " linux.\"UserServer\"\n"
+ "VALUES (\n"
+ " default,\n"
+ " ?,\n"
+ " ?,\n"
+ " linux.get_next_uid(?),\n"
+ " ?,\n"
+ " null,\n"
+ " null,\n"
+ " null,\n"
+ " false,\n"
+ " null,\n"
+ " null,\n"
+ " now(),\n"
+ " true,\n"
+ " " + (user.equals(User.EMAILMON) ? "null::int" : Integer.toString(UserServer.DEFAULT_TRASH_EMAIL_RETENTION)) + ",\n"
+ " " + (user.equals(User.EMAILMON) ? "null::int" : Integer.toString(UserServer.DEFAULT_JUNK_EMAIL_RETENTION)) + ",\n"
+ " ?,\n"
+ " " + UserServer.DEFAULT_SPAM_ASSASSIN_REQUIRED_SCORE + ",\n"
+ " " + (user.equals(User.EMAILMON) ? "null::int" : Integer.toString(UserServer.DEFAULT_SPAM_ASSASSIN_DISCARD_SCORE)) + ",\n"
+ " null\n" // sudo
+ ") RETURNING id",
user,
linuxServer,
linuxServer,
home,
SpamAssassinMode.DEFAULT_SPAMASSASSIN_INTEGRATION_MODE
);
// Notify all clients of the update
Account.Name account = AccountUserHandler.getAccountForUser(conn, user);
invalidateList.addTable(
conn,
Table.TableID.LINUX_SERVER_ACCOUNTS,
account,
linuxServer,
true
);
// If it is a email type, add the default attachment blocks
if(!user.equals(User.EMAILMON) && isUserEmailType(conn, user)) {
conn.executeUpdate(
"insert into email.\"AttachmentBlock\" (\n"
+ " linux_server_account,\n"
+ " extension\n"
+ ") select\n"
+ " ?,\n"
+ " extension\n"
+ "from\n"
+ " email.\"AttachmentType\"\n"
+ "where\n"
+ " is_default_block",
userServer
);
invalidateList.addTable(
conn,
Table.TableID.EMAIL_ATTACHMENT_BLOCKS,
account,
linuxServer,
false
);
}
return userServer;
}
public static int addGroupServer(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
Group.Name group,
int linuxServer,
boolean skipSecurityChecks
) throws IOException, SQLException {
if(
group.equals(Group.FTPONLY)
|| group.equals(Group.MAIL)
|| group.equals(Group.MAILONLY)
) throw new SQLException("Not allowed to add GroupServer for group '"+group+'\'');
Account.Name account = getAccountForGroup(conn, group);
if(!skipSecurityChecks) {
checkAccessGroup(conn, source, "addGroupServer", group);
NetHostHandler.checkAccessHost(conn, source, "addGroupServer", linuxServer);
checkGroupAccessServer(conn, source, "addGroupServer", group, linuxServer);
AccountHandler.checkAccountAccessHost(conn, source, "addGroupServer", account, linuxServer);
}
// Now allocating unique to entire system for server portability between farms
//String farm=ServerHandler.getFarmForServer(conn, linuxServer);
int groupServer = conn.executeIntUpdate(
"INSERT INTO\n"
+ " linux.\"GroupServer\"\n"
+ "VALUES (\n"
+ " default,\n"
+ " ?,\n"
+ " ?,\n"
+ " linux.get_next_gid(?),\n"
+ " now()\n"
+ ") RETURNING id",
group,
linuxServer,
linuxServer
);
// Notify all clients of the update
invalidateList.addTable(
conn,
Table.TableID.LINUX_SERVER_GROUPS,
account,
linuxServer,
true
);
return groupServer;
}
/**
* Gets the group name that exists on a server for the given gid
* or {@code null} if the gid is not allocated to the server.
*/
public static Group.Name getGroupByGid(DatabaseConnection conn, int linuxServer, int gid) throws SQLException {
return conn.executeObjectQuery(Connection.TRANSACTION_READ_COMMITTED,
true,
false,
ObjectFactories.groupNameFactory,
"select name from linux.\"GroupServer\" where ao_server=? and gid=?",
linuxServer,
gid
);
}
/**
* Gets the username that exists on a server for the given uid
* or {@code null} if the uid is not allocated to the server.
*/
public static com.aoindustries.aoserv.client.linux.User.Name getUserByUid(DatabaseConnection conn, int linuxServer, int uid) throws SQLException {
return conn.executeObjectQuery(
Connection.TRANSACTION_READ_COMMITTED,
true,
false,
ObjectFactories.linuxUserNameFactory,
"select username from linux.\"UserServer\" where ao_server=? and uid=?",
linuxServer,
uid
);
}
public static int addSystemGroup(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int linuxServer,
Group.Name group,
int gid
) throws IOException, SQLException {
// This must be a master user with access to the server
com.aoindustries.aoserv.client.master.User mu = MasterServer.getUser(conn, source.getCurrentAdministrator());
if(mu == null) throw new SQLException("Not a master user: " + source.getCurrentAdministrator());
NetHostHandler.checkAccessHost(conn, source, "addSystemGroup", linuxServer);
// The group ID must be in the system group range
if(gid < 0) throw new SQLException("Invalid gid: " + gid);
int gidMin = LinuxServerHandler.getGidMin(conn, linuxServer);
int gidMax = LinuxServerHandler.getGidMax(conn, linuxServer);
// The group ID must not already exist on this server
{
Group.Name existing = getGroupByGid(conn, linuxServer, gid);
if(existing != null) throw new SQLException("Group #" + gid + " already exists on server #" + linuxServer + ": " + existing);
}
// Must be one of the expected patterns for the servers operating system version
int osv = NetHostHandler.getOperatingSystemVersionForHost(conn, linuxServer);
if(
osv == OperatingSystemVersion.CENTOS_7_X86_64
&& (
// Fixed group ids
(group.equals(Group.ROOT) && gid == 0)
|| (group.equals(Group.BIN) && gid == 1)
|| (group.equals(Group.DAEMON) && gid == 2)
|| (group.equals(Group.SYS) && gid == 3)
|| (group.equals(Group.ADM) && gid == 4)
|| (group.equals(Group.TTY) && gid == 5)
|| (group.equals(Group.DISK) && gid == 6)
|| (group.equals(Group.LP) && gid == 7)
|| (group.equals(Group.MEM) && gid == 8)
|| (group.equals(Group.KMEM) && gid == 9)
|| (group.equals(Group.WHEEL) && gid == 10)
|| (group.equals(Group.CDROM) && gid == 11)
|| (group.equals(Group.MAIL) && gid == 12)
|| (group.equals(Group.MAN) && gid == 15)
|| (group.equals(Group.DIALOUT) && gid == 18)
|| (group.equals(Group.FLOPPY) && gid == 19)
|| (group.equals(Group.GAMES) && gid == 20)
|| (group.equals(Group.UTMP) && gid == 22)
|| (group.equals(Group.NAMED) && gid == 25)
|| (group.equals(Group.POSTGRES) && gid == 26)
|| (group.equals(Group.RPCUSER) && gid == 29)
|| (group.equals(Group.MYSQL) && gid == 31)
|| (group.equals(Group.RPC) && gid == 32)
|| (group.equals(Group.TAPE) && gid == 33)
|| (group.equals(Group.UTEMPTER) && gid == 35)
|| (group.equals(Group.VIDEO) && gid == 39)
|| (group.equals(Group.DIP) && gid == 40)
|| (group.equals(Group.MAILNULL) && gid == 47)
|| (group.equals(Group.APACHE) && gid == 48)
|| (group.equals(Group.FTP) && gid == 50)
|| (group.equals(Group.SMMSP) && gid == 51)
|| (group.equals(Group.LOCK) && gid == 54)
|| (group.equals(Group.TSS) && gid == 59)
|| (group.equals(Group.AUDIO) && gid == 63)
|| (group.equals(Group.TCPDUMP) && gid == 72)
|| (group.equals(Group.SSHD) && gid == 74)
|| (group.equals(Group.SASLAUTH) && gid == 76)
|| (group.equals(Group.AWSTATS) && gid == 78)
|| (group.equals(Group.DBUS) && gid == 81)
|| (group.equals(Group.MAILONLY) && gid == 83)
|| (group.equals(Group.SCREEN) && gid == 84)
|| (group.equals(Group.BIRD) && gid == 95)
|| (group.equals(Group.NOBODY) && gid == 99)
|| (group.equals(Group.USERS) && gid == 100)
|| (group.equals(Group.AVAHI_AUTOIPD) && gid == 170)
|| (group.equals(Group.DHCPD) && gid == 177)
|| (group.equals(Group.SYSTEMD_JOURNAL) && gid == 190)
|| (group.equals(Group.SYSTEMD_NETWORK) && gid == 192)
|| (group.equals(Group.NFSNOBODY) && gid == 65534)
|| (
// System groups in range 201 through gidMin - 1
gid >= CENTOS_7_SYS_GID_MIN
&& gid < gidMin
&& (
group.equals(Group.AOSERV_JILTER)
|| group.equals(Group.AOSERV_MASTER)
|| group.equals(Group.AOSERV_XEN_MIGRATION)
|| group.equals(Group.CGRED)
|| group.equals(Group.CHRONY)
|| group.equals(Group.CLAMSCAN)
|| group.equals(Group.CLAMUPDATE)
|| group.equals(Group.INPUT)
|| group.equals(Group.MEMCACHED)
|| group.equals(Group.NGINX)
|| group.equals(Group.POLKITD)
|| group.equals(Group.REDIS)
|| group.equals(Group.SSH_KEYS)
|| group.equals(Group.SYSTEMD_BUS_PROXY)
|| group.equals(Group.SYSTEMD_NETWORK)
|| group.equals(Group.UNBOUND)
|| group.equals(Group.VIRUSGROUP)
)
) || (
// Regular user groups in range gidMin through Group.GID_MAX
gid >= gidMin
&& gid <= gidMax
&& (
group.equals(Group.AOADMIN)
// AOServ Schema
|| group.equals(Group.ACCOUNTING)
|| group.equals(Group.BILLING)
|| group.equals(Group.DISTRIBUTION)
|| group.equals(Group.INFRASTRUCTURE)
|| group.equals(Group.MANAGEMENT)
|| group.equals(Group.MONITORING)
|| group.equals(Group.RESELLER)
)
)
)
) {
int groupServer = conn.executeIntUpdate(
"INSERT INTO\n"
+ " linux.\"GroupServer\"\n"
+ "VALUES (\n"
+ " default,\n"
+ " ?,\n"
+ " ?,\n"
+ " ?,\n"
+ " now()\n"
+ ") RETURNING id",
group,
linuxServer,
gid
);
// Notify all clients of the update
invalidateList.addTable(
conn,
Table.TableID.LINUX_SERVER_GROUPS,
NetHostHandler.getAccountsForHost(conn, linuxServer),
linuxServer,
true
);
return groupServer;
} else {
throw new SQLException("Unexpected system group: " + group + " #" + gid + " on operating system #" + osv);
}
}
static class SystemUser {
static final int ANY_SYSTEM_UID = -1;
static final int ANY_USER_UID = -2;
/**
* The set of allowed system group patterns for CentOS 7.
*/
private static final Map centos7SystemUsers = new HashMap<>();
private static void addCentos7SystemUser(
com.aoindustries.aoserv.client.linux.User.Name user,
int uid,
Group.Name group,
String fullName,
String home,
PosixPath shell,
String sudo
) throws ValidationException {
if(
centos7SystemUsers.put(
user,
new SystemUser(
user,
uid,
group,
InternUtils.intern(Gecos.valueOf(fullName)), null, null, null,
PosixPath.valueOf(home).intern(),
shell,
sudo
)
) != null
) throw new AssertionError("Duplicate username: " + user);
}
static {
try {
try {
// TODO: We should probably have a database table instead of this hard-coded list, same for system groups
addCentos7SystemUser(User.ROOT, 0, Group.ROOT, "root", "/root", Shell.BASH, null);
addCentos7SystemUser(User.BIN, 1, Group.BIN, "bin", "/bin", Shell.NOLOGIN, null);
addCentos7SystemUser(User.DAEMON, 2, Group.DAEMON, "daemon", "/sbin", Shell.NOLOGIN, null);
addCentos7SystemUser(User.ADM, 3, Group.ADM, "adm", "/var/adm", Shell.NOLOGIN, null);
addCentos7SystemUser(User.LP, 4, Group.LP, "lp", "/var/spool/lpd", Shell.NOLOGIN, null);
addCentos7SystemUser(User.SYNC, 5, Group.ROOT, "sync", "/sbin", Shell.SYNC, null);
addCentos7SystemUser(User.SHUTDOWN, 6, Group.ROOT, "shutdown", "/sbin", Shell.SHUTDOWN, null);
addCentos7SystemUser(User.HALT, 7, Group.ROOT, "halt", "/sbin", Shell.HALT, null);
addCentos7SystemUser(User.MAIL, 8, Group.MAIL, "mail", "/var/spool/mail", Shell.NOLOGIN, null);
addCentos7SystemUser(User.OPERATOR, 11, Group.ROOT, "operator", "/root", Shell.NOLOGIN, null);
addCentos7SystemUser(User.GAMES, 12, Group.USERS, "games", "/usr/games", Shell.NOLOGIN, null);
addCentos7SystemUser(User.FTP, 14, Group.FTP, "FTP User", "/var/ftp", Shell.NOLOGIN, null);
addCentos7SystemUser(User.NAMED, 25, Group.NAMED, "Named", "/var/named", Shell.NOLOGIN, null);
addCentos7SystemUser(User.POSTGRES, 26, Group.POSTGRES, "PostgreSQL Server", "/var/lib/pgsql", Shell.BASH, null);
addCentos7SystemUser(User.RPCUSER, 29, Group.RPCUSER, "RPC Service User", "/var/lib/nfs", Shell.NOLOGIN, null);
addCentos7SystemUser(User.MYSQL, 31, Group.MYSQL, "MySQL server", "/var/lib/mysql", Shell.BASH, null);
addCentos7SystemUser(User.RPC, 32, Group.RPC, "Rpcbind Daemon", "/var/lib/rpcbind", Shell.NOLOGIN, null);
addCentos7SystemUser(User.MAILNULL, 47, Group.MAILNULL, null, "/var/spool/mqueue", Shell.NOLOGIN, null);
addCentos7SystemUser(User.APACHE, 48, Group.APACHE, "Apache", "/usr/share/httpd", Shell.NOLOGIN, null);
addCentos7SystemUser(User.SMMSP, 51, Group.SMMSP, null, "/var/spool/mqueue", Shell.NOLOGIN, null);
addCentos7SystemUser(User.TSS, 59, Group.TSS, "Account used by the trousers package to sandbox the tcsd daemon", "/dev/null", Shell.NOLOGIN, null);
addCentos7SystemUser(User.TCPDUMP, 72, Group.TCPDUMP, null, "/", Shell.NOLOGIN, null);
addCentos7SystemUser(User.SSHD, 74, Group.SSHD, "Privilege-separated SSH", "/var/empty/sshd", Shell.NOLOGIN, null);
addCentos7SystemUser(User.CYRUS, 76, Group.MAIL, "Cyrus IMAP Server", "/var/lib/imap", Shell.NOLOGIN, null);
addCentos7SystemUser(User.AWSTATS, 78, Group.AWSTATS, "AWStats Background Log Processing", "/var/opt/awstats", Shell.NOLOGIN, null);
addCentos7SystemUser(User.DBUS, 81, Group.DBUS, "System message bus", "/", Shell.NOLOGIN, null);
addCentos7SystemUser(User.BIRD, 95, Group.BIRD, "BIRD Internet Routing Daemon", "/var/opt/bird", Shell.NOLOGIN, null);
addCentos7SystemUser(User.NOBODY, 99, Group.NOBODY, "Nobody", "/", Shell.NOLOGIN, null);
addCentos7SystemUser(User.AVAHI_AUTOIPD, 170, Group.AVAHI_AUTOIPD, "Avahi IPv4LL Stack", "/var/lib/avahi-autoipd", Shell.NOLOGIN, null);
addCentos7SystemUser(User.DHCPD, 177, Group.DHCPD, "DHCP server", "/", Shell.NOLOGIN, null);
addCentos7SystemUser(User.SYSTEMD_NETWORK, 192, Group.SYSTEMD_NETWORK, "systemd Network Management", "/", Shell.NOLOGIN, null);
addCentos7SystemUser(User.NFSNOBODY, 65534, Group.NFSNOBODY, "Anonymous NFS User", "/var/lib/nfs", Shell.NOLOGIN, null);
addCentos7SystemUser(User.AOSERV_JILTER, ANY_SYSTEM_UID, Group.AOSERV_JILTER, "AOServ Jilter", "/var/opt/aoserv-jilter", Shell.NOLOGIN, null);
addCentos7SystemUser(User.AOSERV_MASTER, ANY_SYSTEM_UID, Group.AOSERV_MASTER, "AOServ Master", "/var/opt/aoserv-master", Shell.NOLOGIN, null);
addCentos7SystemUser(User.CHRONY, ANY_SYSTEM_UID, Group.CHRONY, null, "/var/lib/chrony", Shell.NOLOGIN, null);
addCentos7SystemUser(User.CLAMSCAN, ANY_SYSTEM_UID, Group.CLAMSCAN, "Clamav scanner user", "/", Shell.NOLOGIN, null);
addCentos7SystemUser(User.CLAMUPDATE, ANY_SYSTEM_UID, Group.CLAMUPDATE, "Clamav database update user", "/var/lib/clamav", Shell.NOLOGIN, null);
addCentos7SystemUser(User.MEMCACHED, ANY_SYSTEM_UID, Group.MEMCACHED, "Memcached daemon", "/run/memcached", Shell.NOLOGIN, null);
addCentos7SystemUser(User.NGINX, ANY_SYSTEM_UID, Group.NGINX, "nginx user", "/var/cache/nginx", Shell.NOLOGIN, null);
addCentos7SystemUser(User.POLKITD, ANY_SYSTEM_UID, Group.POLKITD, "User for polkitd", "/", Shell.NOLOGIN, null);
addCentos7SystemUser(User.REDIS, ANY_SYSTEM_UID, Group.REDIS, "Redis Database Server", "/var/lib/redis", Shell.NOLOGIN, null);
addCentos7SystemUser(User.SASLAUTH, ANY_SYSTEM_UID, Group.SASLAUTH, "Saslauthd user", "/run/saslauthd", Shell.NOLOGIN, null);
addCentos7SystemUser(User.SYSTEMD_BUS_PROXY, ANY_SYSTEM_UID, Group.SYSTEMD_BUS_PROXY, "systemd Bus Proxy", "/", Shell.NOLOGIN, null);
addCentos7SystemUser(User.UNBOUND, ANY_SYSTEM_UID, Group.UNBOUND, "Unbound DNS resolver", "/etc/unbound", Shell.NOLOGIN, null);
addCentos7SystemUser(User.AOADMIN, ANY_USER_UID, Group.AOADMIN, "AO Industries Administrator", "/home/aoadmin", Shell.BASH, AOADMIN_SUDO);
addCentos7SystemUser(User.AOSERV_XEN_MIGRATION, ANY_SYSTEM_UID, Group.AOSERV_XEN_MIGRATION, "AOServ Xen Migration", "/var/opt/aoserv-xen-migration", Shell.BASH, AOSERV_XEN_MIGRATION_SUDO);
// AOServ Schema:
addCentos7SystemUser(User.ACCOUNTING, ANY_USER_UID, Group.ACCOUNTING, "masterdb access", "/home/accounting", Shell.BASH, null);
addCentos7SystemUser(User.BILLING, ANY_USER_UID, Group.BILLING, "masterdb access", "/home/billing", Shell.BASH, null);
addCentos7SystemUser(User.DISTRIBUTION, ANY_USER_UID, Group.DISTRIBUTION, "masterdb access", "/home/distribution", Shell.BASH, null);
addCentos7SystemUser(User.INFRASTRUCTURE, ANY_USER_UID, Group.INFRASTRUCTURE, "masterdb access", "/home/infrastructure", Shell.BASH, null);
addCentos7SystemUser(User.MANAGEMENT, ANY_USER_UID, Group.MANAGEMENT, "masterdb access", "/home/management", Shell.BASH, null);
addCentos7SystemUser(User.MONITORING, ANY_USER_UID, Group.MONITORING, "masterdb access", "/home/monitoring", Shell.BASH, null);
addCentos7SystemUser(User.RESELLER, ANY_USER_UID, Group.RESELLER, "masterdb access", "/home/reseller", Shell.BASH, null);
} catch(ValidationException e) {
throw new AssertionError("These hard-coded values are valid", e);
}
} catch(Throwable t) {
t.printStackTrace(System.err);
if(t instanceof RuntimeException) throw (RuntimeException)t;
if(t instanceof Error) throw (Error)t;
throw new RuntimeException(t);
}
}
final com.aoindustries.aoserv.client.linux.User.Name user;
final int uid;
final Group.Name group;
final Gecos fullName;
final Gecos officeLocation;
final Gecos officePhone;
final Gecos homePhone;
final PosixPath home;
final PosixPath shell;
final String sudo;
SystemUser(
com.aoindustries.aoserv.client.linux.User.Name user,
int uid,
Group.Name group,
Gecos fullName,
Gecos officeLocation,
Gecos officePhone,
Gecos homePhone,
PosixPath home,
PosixPath shell,
String sudo
) {
this.user = user;
this.uid = uid;
this.group = group;
this.fullName = fullName;
this.officeLocation = officeLocation;
this.officePhone = officePhone;
this.homePhone = homePhone;
this.home = home;
this.shell = shell;
this.sudo = sudo;
}
}
public static int addSystemUser(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int linuxServer,
com.aoindustries.aoserv.client.linux.User.Name user,
int uid,
int gid,
Gecos fullName,
Gecos officeLocation,
Gecos officePhone,
Gecos homePhone,
PosixPath home,
PosixPath shell
) throws IOException, SQLException {
// This must be a master user with access to the server
com.aoindustries.aoserv.client.master.User mu = MasterServer.getUser(conn, source.getCurrentAdministrator());
if(mu == null) throw new SQLException("Not a master user: " + source.getCurrentAdministrator());
NetHostHandler.checkAccessHost(conn, source, "addSystemUser", linuxServer);
// The user ID must be in the system user range
if(uid < 0) throw new SQLException("Invalid uid: " + uid);
int uidMin = LinuxServerHandler.getUidMin(conn, linuxServer);
int uidMax = LinuxServerHandler.getUidMax(conn, linuxServer);
// The user ID must not already exist on this server
{
com.aoindustries.aoserv.client.linux.User.Name existing = getUserByUid(conn, linuxServer, uid);
if(existing != null) throw new SQLException("User #" + uid + " already exists on server #" + linuxServer + ": " + existing);
}
// Get the group name for the requested gid
Group.Name group = getGroupByGid(conn, linuxServer, gid);
if(group == null) throw new SQLException("Group #" + gid + " does not exist on server #" + linuxServer);
// Must be one of the expected patterns for the servers operating system version
int osv = NetHostHandler.getOperatingSystemVersionForHost(conn, linuxServer);
SystemUser systemUser;
if(
osv == OperatingSystemVersion.CENTOS_7_X86_64
&& (systemUser = SystemUser.centos7SystemUsers.get(user)) != null
) {
if(systemUser.uid == SystemUser.ANY_SYSTEM_UID) {
// System users in range 201 through uidMin - 1
if(uid < CENTOS_7_SYS_UID_MIN || uid >= uidMin) throw new SQLException("Invalid system uid: " + uid);
} else if(systemUser.uid == SystemUser.ANY_USER_UID) {
// Regular users in range uidMin through User.UID_MAX
if(uid < uidMin || uid > uidMax) throw new SQLException("Invalid regular user uid: " + uid);
} else {
// UID must match exactly
if(uid != systemUser.uid) throw new SQLException("Unexpected system uid: " + uid + " != " + systemUser.uid);
}
// Check other fields match
if(!Objects.equals(group, systemUser.group)) throw new SQLException("Unexpected system group: " + group + " != " + systemUser.group);
if(!Objects.equals(fullName, systemUser.fullName)) throw new SQLException("Unexpected system fullName: " + fullName + " != " + systemUser.fullName);
if(!Objects.equals(officeLocation, systemUser.officeLocation)) throw new SQLException("Unexpected system officeLocation: " + officeLocation + " != " + systemUser.officeLocation);
if(!Objects.equals(officePhone, systemUser.officePhone)) throw new SQLException("Unexpected system officePhone: " + officePhone + " != " + systemUser.officePhone);
if(!Objects.equals(homePhone, systemUser.homePhone)) throw new SQLException("Unexpected system homePhone: " + homePhone + " != " + systemUser.homePhone);
if(!Objects.equals(home, systemUser.home)) throw new SQLException("Unexpected system home: " + home + " != " + systemUser.home);
if(!Objects.equals(shell, systemUser.shell)) throw new SQLException("Unexpected system shell: " + shell + " != " + systemUser.shell);
// Add to database
int userServer = conn.executeIntUpdate(
"INSERT INTO\n"
+ " linux.\"UserServer\"\n"
+ "VALUES (\n"
+ " default,\n" // id
+ " ?,\n" // username
+ " ?,\n" // ao_server
+ " ?,\n" // uid
+ " ?,\n" // home
+ " null,\n" // autoresponder_from
+ " null,\n" // autoresponder_subject
+ " null,\n" // autoresponder_path
+ " false,\n" // is_autoresponder_enabled
+ " null,\n" // disable_log
+ " null,\n" // predisable_password
+ " now(),\n" // created
+ " true,\n" // use_inbox
+ " null,\n" // trash_email_retention
+ " null,\n" // junk_email_retention
+ " ?,\n" // sa_integration_mode
+ " "+UserServer.DEFAULT_SPAM_ASSASSIN_REQUIRED_SCORE+",\n"
+ " null,\n" // sa_discard_score
+ " ?\n" // sudo
+ ") RETURNING id",
user,
linuxServer,
uid,
home,
SpamAssassinMode.NONE,
systemUser.sudo
);
// Notify all clients of the update
invalidateList.addTable(
conn,
Table.TableID.LINUX_SERVER_ACCOUNTS,
NetHostHandler.getAccountsForHost(conn, linuxServer),
linuxServer,
true
);
return userServer;
} else {
throw new SQLException("Unexpected system user: " + user + " #" + uid + " on operating system #" + osv);
}
}
/**
* Copies the contents of a home directory from one server to another.
*/
public static long copyHomeDirectory(
DatabaseConnection conn,
RequestSource source,
int from_userServer,
int to_server
) throws IOException, SQLException {
checkAccessUserServer(conn, source, "copyHomeDirectory", from_userServer);
com.aoindustries.aoserv.client.linux.User.Name user = getUserForUserServer(conn, from_userServer);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to copy User named '"+User.MAIL+'\'');
int from_server=getServerForUserServer(conn, from_userServer);
int to_lsa=conn.executeIntQuery(
"select id from linux.\"UserServer\" where username=? and ao_server=?",
user,
to_server
);
checkAccessUserServer(conn, source, "copyHomeDirectory", to_lsa);
String type=getTypeForUser(conn, user);
if(
!type.equals(UserType.USER)
&& !type.equals(UserType.EMAIL)
&& !type.equals(UserType.FTPONLY)
) throw new SQLException("Not allowed to copy LinuxAccounts of type '"+type+"', username="+user);
AOServDaemonConnector fromDaemonConnector = DaemonHandler.getDaemonConnector(conn, from_server);
AOServDaemonConnector toDaemonConnector = DaemonHandler.getDaemonConnector(conn, to_server);
conn.releaseConnection();
long byteCount = fromDaemonConnector.copyHomeDirectory(user, toDaemonConnector);
return byteCount;
}
/**
* Copies a password from one linux account to another
*/
public static void copyUserServerPassword(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int from_userServer,
int to_userServer
) throws IOException, SQLException {
checkAccessUserServer(conn, source, "copyLinuxServerAccountPassword", from_userServer);
if(isUserServerDisabled(conn, from_userServer)) throw new SQLException("Unable to copy UserServer password, from account disabled: "+from_userServer);
com.aoindustries.aoserv.client.linux.User.Name from_user = getUserForUserServer(conn, from_userServer);
if(from_user.equals(User.MAIL)) throw new SQLException("Not allowed to copy the password from User named '"+User.MAIL+'\'');
checkAccessUserServer(conn, source, "copyLinuxServerAccountPassword", to_userServer);
if(isUserServerDisabled(conn, to_userServer)) throw new SQLException("Unable to copy UserServer password, to account disabled: "+to_userServer);
com.aoindustries.aoserv.client.linux.User.Name to_user = getUserForUserServer(conn, to_userServer);
if(to_user.equals(User.MAIL)) throw new SQLException("Not allowed to copy the password to User named '"+User.MAIL+'\'');
int from_server=getServerForUserServer(conn, from_userServer);
int to_server=getServerForUserServer(conn, to_userServer);
String from_type=getTypeForUser(conn, from_user);
if(
!from_type.equals(UserType.APPLICATION)
&& !from_type.equals(UserType.USER)
&& !from_type.equals(UserType.EMAIL)
&& !from_type.equals(UserType.FTPONLY)
) throw new SQLException("Not allowed to copy passwords from LinuxAccounts of type '"+from_type+"', username="+from_user);
String to_type=getTypeForUser(conn, to_user);
if(
!to_type.equals(UserType.APPLICATION)
&& !to_type.equals(UserType.USER)
&& !to_type.equals(UserType.EMAIL)
&& !to_type.equals(UserType.FTPONLY)
) throw new SQLException("Not allowed to copy passwords to LinuxAccounts of type '"+to_type+"', username="+to_user);
AOServDaemonConnector fromDemonConnector = DaemonHandler.getDaemonConnector(conn, from_server);
AOServDaemonConnector toDaemonConnector = DaemonHandler.getDaemonConnector(conn, to_server);
conn.releaseConnection();
Tuple2 enc_password = fromDemonConnector.getEncryptedLinuxAccountPassword(from_user);
toDaemonConnector.setEncryptedLinuxAccountPassword(to_user, enc_password.getElement1(), enc_password.getElement2());
//Account.Name from_account=UsernameHandler.getAccountForUsername(conn, from_username);
//Account.Name to_account=UsernameHandler.getAccountForUsername(conn, to_username);
}
public static void disableUser(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int disableLog,
com.aoindustries.aoserv.client.linux.User.Name user
) throws IOException, SQLException {
AccountHandler.checkAccessDisableLog(conn, source, "disableUser", disableLog, false);
checkAccessUser(conn, source, "disableUser", user);
if(isUserDisabled(conn, user)) throw new SQLException("linux.User is already disabled: "+user);
IntList lsas=getUserServersForUser(conn, user);
for(int c=0;c= 2 && !User.NO_PASSWORD_CONFIG_VALUE.equals(crypted);
}
public static int isUserServerProcmailManual(
DatabaseConnection conn,
RequestSource source,
int userServer
) throws IOException, SQLException {
checkAccessUserServer(conn, source, "isUserServerProcmailManual", userServer);
int linuxServer = getServerForUserServer(conn, userServer);
if(DaemonHandler.isDaemonAvailable(linuxServer)) {
try {
AOServDaemonConnector daemonConnector = DaemonHandler.getDaemonConnector(conn, linuxServer);
conn.releaseConnection();
return daemonConnector.isProcmailManual(userServer) ? AoservProtocol.TRUE : AoservProtocol.FALSE;
} catch(IOException err) {
DaemonHandler.flagDaemonAsDown(linuxServer);
return AoservProtocol.SERVER_DOWN;
}
} else {
return AoservProtocol.SERVER_DOWN;
}
}
public static void removeUser(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
com.aoindustries.aoserv.client.linux.User.Name user
) throws IOException, SQLException {
checkAccessUser(conn, source, "removeUser", user);
removeUser(conn, invalidateList, user);
}
public static void removeUser(
DatabaseConnection conn,
InvalidateList invalidateList,
com.aoindustries.aoserv.client.linux.User.Name user
) throws IOException, SQLException {
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to remove User with username '"+User.MAIL+'\'');
// Detach the linux account from its autoresponder address
IntList linuxServers = getServersForUser(conn, user);
for(int c=0;c 0;
// Delete the account from all servers
// Get the values for later use
for(int c=0;c 0;
// Delete from the database
conn.executeUpdate("delete from linux.\"User\" where username=?", user);
Account.Name account = AccountUserHandler.getAccountForUser(conn, user);
if(ftpModified) invalidateList.addTable(conn, Table.TableID.FTP_GUEST_USERS, account, linuxServers, false);
if(groupAccountModified) invalidateList.addTable(conn, Table.TableID.LINUX_GROUP_ACCOUNTS, account, linuxServers, false);
invalidateList.addTable(conn, Table.TableID.LINUX_ACCOUNTS, account, linuxServers, false);
}
public static void removeGroup(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
Group.Name group
) throws IOException, SQLException {
checkAccessGroup(conn, source, "removeGroup", group);
removeGroup(conn, invalidateList, group);
}
public static void removeGroup(
DatabaseConnection conn,
InvalidateList invalidateList,
Group.Name group
) throws IOException, SQLException {
if(
group.equals(Group.FTPONLY)
|| group.equals(Group.MAIL)
|| group.equals(Group.MAILONLY)
) throw new SQLException("Not allowed to remove Group named '"+group+"'");
// Must not be the primary group for any User
int primaryCount=conn.executeIntQuery("select count(*) from linux.\"GroupUser\" where \"group\"=? and \"isPrimary\"", group);
if(primaryCount>0) throw new SQLException("linux_group.name="+group+" is the primary group for "+primaryCount+" Linux "+(primaryCount==1?"account":"accounts"));
// Get the values for later use
Account.Name account = getAccountForGroup(conn, group);
IntList linuxServers = getServersForGroup(conn, group);
for(int c=0;c0;
if(groupAccountsModified) conn.executeUpdate("delete from linux.\"GroupUser\" where \"group\"=?", group);
// Delete from the database
conn.executeUpdate("delete from linux.\"Group\" where name=?", group);
// Notify all clients of the update
if(linuxServers.size()>0) invalidateList.addTable(conn, Table.TableID.LINUX_SERVER_GROUPS, account, linuxServers, false);
if(groupAccountsModified) invalidateList.addTable(conn, Table.TableID.LINUX_GROUP_ACCOUNTS, account, linuxServers, false);
invalidateList.addTable(conn, Table.TableID.LINUX_GROUPS, account, linuxServers, false);
}
public static void removeGroupUser(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int groupUser
) throws IOException, SQLException {
checkAccessGroupUser(conn, source, "removeGroupUser", groupUser);
// Must not be a primary group
boolean isPrimary=conn.executeBooleanQuery("select \"isPrimary\" from linux.\"GroupUser\" where id=?", groupUser);
if(isPrimary) throw new SQLException("linux.GroupUser.id="+groupUser+" is a primary group");
// Must be needingful not by SharedTomcatSite to be tying to SharedTomcat please
int useCount = conn.executeIntQuery(
"select count(*) from linux.\"GroupUser\" lga, "+
"linux.\"UserServer\" lsa, "+
"\"web.tomcat\".\"SharedTomcat\" hst, "+
"\"web.tomcat\".\"SharedTomcatSite\" htss, "+
"web.\"Site\" hs "+
"where lga.\"user\" = lsa.username and "+
"lsa.id = hst.linux_server_account and "+
"htss.tomcat_site = hs.id and "+
"lga.\"group\" = hs.linux_group and "+
"hst.id = htss.httpd_shared_tomcat and "+
"lga.id = ?",
groupUser
);
if (useCount==0) {
useCount = conn.executeIntQuery(
"select count(*) from linux.\"GroupUser\" lga, "+
"linux.\"GroupServer\" lsg, "+
"\"web.tomcat\".\"SharedTomcat\" hst, "+
"\"web.tomcat\".\"SharedTomcatSite\" htss, "+
"web.\"Site\" hs "+
"where lga.\"group\" = lsg.name and "+
"lsg.id = hst.linux_server_group and "+
"htss.tomcat_site = hs.id and "+
"lga.\"user\" = hs.linux_account and "+
"hst.id = htss.httpd_shared_tomcat and "+
"lga.id = ?",
groupUser
);
}
if (useCount>0) throw new SQLException("linux_group_account("+groupUser+") has been used by "+useCount+" web.tomcat.SharedTomcatSite.");
// Get the values for later use
List accounts = getAccountsForGroupUser(conn, groupUser);
IntList linuxServers = getServersForGroupUser(conn, groupUser);
// Delete the group relations for this group
conn.executeUpdate("delete from linux.\"GroupUser\" where id=?", groupUser);
// Notify all clients of the update
invalidateList.addTable(conn, Table.TableID.LINUX_GROUP_ACCOUNTS, accounts, linuxServers, false);
}
/* Unused 2019-07-10:
public static void removeUnusedAlternateGroupUser(
DatabaseConnection conn,
InvalidateList invalidateList,
Group.Name group,
com.aoindustries.aoserv.client.linux.User.Name user
) throws IOException, SQLException {
int groupUser = conn.executeIntQuery(
"select\n"
+ " coalesce(\n"
+ " (\n"
+ " select\n"
+ " lga.id\n"
+ " from\n"
+ " linux.\"GroupUser\" lga\n"
+ " where\n"
+ " lga.\"group\"=?\n"
+ " and lga.\"user\"=?\n"
+ " and not lga.\"isPrimary\"\n"
+ " and (\n"
+ " select\n"
+ " htss.tomcat_site\n"
+ " from\n"
+ " linux.\"UserServer\" lsa,\n"
+ " \"web.tomcat\".\"SharedTomcat\" hst,\n"
+ " \"web.tomcat\".\"SharedTomcatSite\" htss,\n"
+ " web.\"Site\" hs\n"
+ " where\n"
+ " lga.\"user\"=lsa.username\n"
+ " and lsa.id=hst.linux_server_account\n"
+ " and hst.id=htss.httpd_shared_tomcat\n"
+ " and htss.tomcat_site=hs.id\n"
+ " and hs.linux_group=lga.\"group\"\n"
+ " limit 1\n"
+ " ) is null\n"
+ " and (\n"
+ " select\n"
+ " htss.tomcat_site\n"
+ " from\n"
+ " linux.\"GroupServer\" lsg,\n"
+ " \"web.tomcat\".\"SharedTomcat\" hst,\n"
+ " \"web.tomcat\".\"SharedTomcatSite\" htss,\n"
+ " web.\"Site\" hs\n"
+ " where\n"
+ " lga.\"group\"=lsg.name\n"
+ " and lsg.id=hst.linux_server_group\n"
+ " and hst.id=htss.httpd_shared_tomcat\n"
+ " and htss.tomcat_site=hs.id\n"
+ " and hs.linux_account=lga.\"user\"\n"
+ " limit 1\n"
+ " ) is null\n"
+ " ),\n"
+ " -1\n"
+ " )",
group,
user
);
if(groupUser!=-1) {
// Get the values for later use
List accounts = getAccountsForGroupUser(conn, groupUser);
IntList linuxServers = getServersForGroupUser(conn, groupUser);
conn.executeUpdate("delete from linux.\"GroupUser\" where id=?", groupUser);
// Notify all clients of the update
invalidateList.addTable(conn, Table.TableID.LINUX_GROUP_ACCOUNTS, accounts, linuxServers, false);
}
}
*/
public static void removeUserServer(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int userServer
) throws IOException, SQLException {
checkAccessUserServer(conn, source, "removeUserServer", userServer);
removeUserServer(conn, invalidateList, userServer);
}
public static void removeUserServer(
DatabaseConnection conn,
InvalidateList invalidateList,
int userServer
) throws IOException, SQLException {
com.aoindustries.aoserv.client.linux.User.Name user = getUserForUserServer(conn, userServer);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to remove UserServer for user '"+User.MAIL+'\'');
int linuxServer = getServerForUserServer(conn, userServer);
int uidMin = LinuxServerHandler.getUidMin(conn, linuxServer);
// The UID must be a user UID
int uid=getUidForUserServer(conn, userServer);
if(uid < uidMin) throw new SQLException("Not allowed to remove a system UserServer: id="+userServer+", uid="+uid);
// Must not contain a CVS repository
String home=conn.executeStringQuery("select home from linux.\"UserServer\" where id=?", userServer);
int count=conn.executeIntQuery(
"select\n"
+ " count(*)\n"
+ "from\n"
+ " scm.\"CvsRepository\" cr\n"
+ "where\n"
+ " linux_server_account=?\n"
+ " and (\n"
+ " path=?\n"
+ " or substring(path from 1 for "+(home.length()+1)+")=?\n"
+ " )",
userServer,
home,
home+'/'
);
if(count>0) throw new SQLException("Home directory on "+linuxServer+" contains "+count+" CVS "+(count==1?"repository":"repositories")+": "+home);
// Delete the email configurations that depend on this account
IntList addresses=conn.executeIntListQuery("select email_address from email.\"InboxAddress\" where linux_server_account=?", userServer);
int size=addresses.size();
boolean addressesModified=size>0;
for(int c=0;c 0) {
invalidateList.addTable(conn, Table.TableID.EMAIL_ATTACHMENT_BLOCKS, account, linuxServer, false);
}
// Delete the account from the server
conn.executeUpdate("delete from linux.\"UserServer\" where id=?", userServer);
invalidateList.addTable(conn, Table.TableID.LINUX_SERVER_ACCOUNTS, account, linuxServer, true);
// Notify all clients of the update
if(addressesModified) {
invalidateList.addTable(conn, Table.TableID.LINUX_ACC_ADDRESSES, account, linuxServer, false);
invalidateList.addTable(conn, Table.TableID.EMAIL_ADDRESSES, account, linuxServer, false);
}
}
public static void removeGroupServer(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int groupServer
) throws IOException, SQLException {
checkAccessGroupServer(conn, source, "removeGroupServer", groupServer);
removeGroupServer(conn, invalidateList, groupServer);
}
public static void removeGroupServer(
DatabaseConnection conn,
InvalidateList invalidateList,
int groupServer
) throws IOException, SQLException {
Group.Name group = getGroupForGroupServer(conn, groupServer);
if(
group.equals(Group.FTPONLY)
|| group.equals(Group.MAIL)
|| group.equals(Group.MAILONLY)
) throw new SQLException("Not allowed to remove GroupServer for group '"+group+"'");
// Get the server this group is on
Account.Name account = getAccountForGroupServer(conn, groupServer);
int linuxServer = getServerForGroupServer(conn, groupServer);
// Must not be the primary group for any UserServer on the same server
int primaryCount=conn.executeIntQuery(
"select\n"
+ " count(*)\n"
+ "from\n"
+ " linux.\"GroupServer\" lsg\n"
+ " inner join linux.\"GroupUser\" lga on lsg.name=lga.\"group\"\n"
+ " inner join linux.\"UserServer\" lsa on lga.\"user\"=lsa.username\n"
+ " inner join net.\"Host\" se on lsg.ao_server=se.id\n"
+ "where\n"
+ " lsg.id=?\n"
+ " and lga.\"isPrimary\"\n"
+ " and (\n"
+ " lga.\"operatingSystemVersion\" is null\n"
+ " or lga.\"operatingSystemVersion\" = se.operating_system_version\n"
+ " )\n"
+ " and lsg.ao_server=lsa.ao_server",
groupServer
);
if(primaryCount>0) throw new SQLException("linux_server_group.id="+groupServer+" is the primary group for "+primaryCount+" Linux server "+(primaryCount==1?"account":"accounts")+" on "+linuxServer);
// Delete from the database
conn.executeUpdate("delete from linux.\"GroupServer\" where id=?", groupServer);
// Notify all clients of the update
invalidateList.addTable(conn, Table.TableID.LINUX_SERVER_GROUPS, account, linuxServer, true);
}
public static void setAutoresponder(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int userServer,
int from,
String subject,
String content,
boolean enabled
) throws IOException, SQLException {
checkAccessUserServer(conn, source, "setAutoresponder", userServer);
if(isUserServerDisabled(conn, userServer)) throw new SQLException("Unable to set autoresponder, UserServer disabled: "+userServer);
com.aoindustries.aoserv.client.linux.User.Name user = getUserForUserServer(conn, userServer);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to set autoresponder for user '"+User.MAIL+'\'');
String type=getTypeForUser(conn, user);
if(
!type.equals(UserType.EMAIL)
&& !type.equals(UserType.USER)
) throw new SQLException("Not allowed to set autoresponder for this type of account: "+type);
// The from must be on this account
if(from!=-1) {
int fromLSA=conn.executeIntQuery("select linux_server_account from email.\"InboxAddress\" where id=?", from);
if(fromLSA!=userServer) throw new SQLException("((linux_acc_address.id="+from+").linux_server_account="+fromLSA+")!=((linux_server_account.id="+userServer+").username="+user+")");
}
Account.Name account = AccountUserHandler.getAccountForUser(conn, user);
int linuxServer = getServerForUserServer(conn, userServer);
PosixPath path;
if(content==null && !enabled) {
path = null;
} else {
path = conn.executeObjectQuery(ObjectFactories.posixPathFactory,
"select coalesce(autoresponder_path, home || '/.autorespond.txt') from linux.\"UserServer\" where id=?",
userServer
);
}
int uid;
int gid;
if(!enabled) {
uid=-1;
gid=-1;
} else {
uid = getUidForUserServer(conn, userServer);
gid = conn.executeIntQuery(
"select\n"
+ " lsg.gid\n"
+ "from\n"
+ " linux.\"UserServer\" lsa\n"
+ " inner join linux.\"GroupUser\" lga on lsa.username=lga.\"user\"\n"
+ " inner join linux.\"GroupServer\" lsg on lga.\"group\"=lsg.name\n"
+ " inner join net.\"Host\" se on lsa.ao_server=se.id\n"
+ "where\n"
+ " lsa.id=?\n"
+ " and lga.\"isPrimary\"\n"
+ " and (\n"
+ " lga.\"operatingSystemVersion\" is null\n"
+ " or lga.\"operatingSystemVersion\" = se.operating_system_version\n"
+ " )\n"
+ " and lsa.ao_server=lsg.ao_server",
userServer
);
}
try (
PreparedStatement pstmt = conn.getConnection(Connection.TRANSACTION_READ_COMMITTED, false).prepareStatement(
"update\n"
+ " linux.\"UserServer\"\n"
+ "set\n"
+ " autoresponder_from=?,\n"
+ " autoresponder_subject=?,\n"
+ " autoresponder_path=?,\n"
+ " is_autoresponder_enabled=?\n"
+ "where\n"
+ " id=?"
)
) {
try {
if(from==-1) pstmt.setNull(1, Types.INTEGER);
else pstmt.setInt(1, from);
pstmt.setString(2, subject);
pstmt.setString(3, Objects.toString(path, null));
pstmt.setBoolean(4, enabled);
pstmt.setInt(5, userServer);
pstmt.executeUpdate();
} catch(SQLException err) {
System.err.println("Error from update: "+pstmt.toString());
throw err;
}
}
// Store the content on the server
if(path!=null) {
AOServDaemonConnector daemonConnector = DaemonHandler.getDaemonConnector(conn, linuxServer);
conn.releaseConnection();
daemonConnector.setAutoresponderContent(path, content==null?"":content, uid, gid);
}
// Notify all clients of the update
invalidateList.addTable(conn, Table.TableID.LINUX_SERVER_ACCOUNTS, account, linuxServer, false);
}
/**
* Gets the contents of a user cron table.
*/
public static void setCronTable(
DatabaseConnection conn,
RequestSource source,
int userServer,
String cronTable
) throws IOException, SQLException {
checkAccessUserServer(conn, source, "setCronTable", userServer);
if(isUserServerDisabled(conn, userServer)) throw new SQLException("Unable to set cron table, UserServer disabled: "+userServer);
com.aoindustries.aoserv.client.linux.User.Name user = getUserForUserServer(conn, userServer);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to set the cron table for User named '"+User.MAIL+'\'');
String type=getTypeForUser(conn, user);
if(
!type.equals(UserType.USER)
) throw new SQLException("Not allowed to set the cron table for LinuxAccounts of type '"+type+"', username="+user);
int linuxServer = getServerForUserServer(conn, userServer);
AOServDaemonConnector daemonConnector = DaemonHandler.getDaemonConnector(conn, linuxServer);
conn.releaseConnection();
daemonConnector.setCronTable(user, cronTable);
}
public static void setUserHomePhone(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
com.aoindustries.aoserv.client.linux.User.Name user,
Gecos phone
) throws IOException, SQLException {
checkAccessUser(conn, source, "setUserHomePhone", user);
if(isUserDisabled(conn, user)) throw new SQLException("Unable to set home phone number, User disabled: "+user);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to set home phone number for user '"+User.MAIL+'\'');
Account.Name account = AccountUserHandler.getAccountForUser(conn, user);
IntList linuxServers = getServersForUser(conn, user);
conn.executeUpdate("update linux.\"User\" set home_phone=? where username=?", phone, user);
// Notify all clients of the update
invalidateList.addTable(conn, Table.TableID.LINUX_ACCOUNTS, account, linuxServers, false);
}
public static void setUserFullName(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
com.aoindustries.aoserv.client.linux.User.Name user,
Gecos name
) throws IOException, SQLException {
checkAccessUser(conn, source, "setUserFullName", user);
if(isUserDisabled(conn, user)) throw new SQLException("Unable to set full name, User disabled: "+user);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to set LinuxAccountName for user '"+User.MAIL+'\'');
Account.Name account = AccountUserHandler.getAccountForUser(conn, user);
IntList linuxServers = getServersForUser(conn, user);
conn.executeUpdate("update linux.\"User\" set name=? where username=?", name, user);
// Notify all clients of the update
invalidateList.addTable(conn, Table.TableID.LINUX_ACCOUNTS, account, linuxServers, false);
}
public static void setUserOfficeLocation(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
com.aoindustries.aoserv.client.linux.User.Name user,
Gecos location
) throws IOException, SQLException {
checkAccessUser(conn, source, "setUserOfficeLocation", user);
if(isUserDisabled(conn, user)) throw new SQLException("Unable to set office location, User disabled: "+user);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to set office location for user '"+User.MAIL+'\'');
Account.Name account = AccountUserHandler.getAccountForUser(conn, user);
IntList linuxServers = getServersForUser(conn, user);
conn.executeUpdate("update linux.\"User\" set office_location=? where username=?", location, user);
// Notify all clients of the update
invalidateList.addTable(conn, Table.TableID.LINUX_ACCOUNTS, account, linuxServers, false);
}
public static void setUserOfficePhone(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
com.aoindustries.aoserv.client.linux.User.Name user,
Gecos phone
) throws IOException, SQLException {
checkAccessUser(conn, source, "setUserOfficePhone", user);
if(isUserDisabled(conn, user)) throw new SQLException("Unable to set office phone number, User disabled: "+user);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to set office phone number for user '"+User.MAIL+'\'');
Account.Name account = AccountUserHandler.getAccountForUser(conn, user);
IntList linuxServers = getServersForUser(conn, user);
conn.executeUpdate("update linux.\"User\" set office_phone=? where username=?", phone, user);
// Notify all clients of the update
invalidateList.addTable(conn, Table.TableID.LINUX_ACCOUNTS, account, linuxServers, false);
}
public static void setUserShell(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
com.aoindustries.aoserv.client.linux.User.Name user,
PosixPath shell
) throws IOException, SQLException {
checkAccessUser(conn, source, "setUserShell", user);
if(isUserDisabled(conn, user)) throw new SQLException("Unable to set shell, User disabled: "+user);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to set shell for account named '"+User.MAIL+'\'');
String type=getTypeForUser(conn, user);
if(!UserType.isAllowedShell(type, shell)) throw new SQLException("Shell '"+shell+"' not allowed for Linux accounts with the type '"+type+'\'');
Account.Name account = AccountUserHandler.getAccountForUser(conn, user);
IntList linuxServers = getServersForUser(conn, user);
conn.executeUpdate("update linux.\"User\" set shell=? where username=?", shell, user);
// Notify all clients of the update
invalidateList.addTable(conn, Table.TableID.LINUX_ACCOUNTS, account, linuxServers, false);
}
public static void setUserServerPassword(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int userServer,
String password
) throws IOException, SQLException {
AccountHandler.checkPermission(conn, source, "setUserServerPassword", Permission.Name.set_linux_server_account_password);
checkAccessUserServer(conn, source, "setUserServerPassword", userServer);
if(isUserServerDisabled(conn, userServer)) throw new SQLException("Unable to set UserServer password, account disabled: "+userServer);
com.aoindustries.aoserv.client.linux.User.Name user = getUserForUserServer(conn, userServer);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to set password for UserServer named '"+User.MAIL+"': "+userServer);
String type=conn.executeStringQuery("select type from linux.\"User\" where username=?", user);
// Make sure passwords can be set before doing a strength check
if(!UserType.canSetPassword(type)) throw new SQLException("Passwords may not be set for UserType="+type);
if(password!=null && password.length()>0) {
// Perform the password check here, too.
List results = User.checkPassword(user, type, password);
if(PasswordChecker.hasResults(results)) throw new SQLException("Invalid password: "+PasswordChecker.getResultsString(results).replace('\n', '|'));
}
Account.Name account = AccountUserHandler.getAccountForUser(conn, user);
int linuxServer = getServerForUserServer(conn, userServer);
try {
AOServDaemonConnector daemonConnector = DaemonHandler.getDaemonConnector(conn, linuxServer);
conn.releaseConnection();
daemonConnector.setLinuxServerAccountPassword(user, password);
} catch(IOException | SQLException err) {
System.err.println("Unable to set linux account password for "+user+" on "+linuxServer);
throw err;
}
// Update the linux.Server table for emailmon and ftpmon
/*if(username.equals(User.EMAILMON)) {
conn.executeUpdate("update linux.\"Server\" set emailmon_password=? where server=?", password==null||password.length()==0?null:password, linuxServer);
invalidateList.addTable(conn, Table.TableID.AO_SERVERS, ServerHandler.getAccountsForHost(conn, linuxServer), linuxServer, false);
} else if(username.equals(User.FTPMON)) {
conn.executeUpdate("update linux.\"Server\" set ftpmon_password=? where server=?", password==null||password.length()==0?null:password, linuxServer);
invalidateList.addTable(conn, Table.TableID.AO_SERVERS, ServerHandler.getAccountsForHost(conn, linuxServer), linuxServer, false);
}*/
}
public static void setUserServerPredisablePassword(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int userServer,
String password
) throws IOException, SQLException {
checkAccessUserServer(conn, source, "setUserServerPredisablePassword", userServer);
if(password==null) {
if(isUserServerDisabled(conn, userServer)) throw new SQLException("Unable to clear UserServer predisable password, account disabled: "+userServer);
} else {
if(!isUserServerDisabled(conn, userServer)) throw new SQLException("Unable to set UserServer predisable password, account not disabled: "+userServer);
}
// Update the database
conn.executeUpdate(
"update linux.\"UserServer\" set predisable_password=? where id=?",
password,
userServer
);
invalidateList.addTable(
conn,
Table.TableID.LINUX_SERVER_ACCOUNTS,
getAccountForUserServer(conn, userServer),
getServerForUserServer(conn, userServer),
false
);
}
public static void setUserServerJunkEmailRetention(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int userServer,
int days
) throws IOException, SQLException {
// Security checks
checkAccessUserServer(conn, source, "setUserServerJunkEmailRetention", userServer);
com.aoindustries.aoserv.client.linux.User.Name user = getUserForUserServer(conn, userServer);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to set the junk email retention for User named '"+User.MAIL+'\'');
// Update the database
if(days==-1) {
conn.executeUpdate(
"update linux.\"UserServer\" set junk_email_retention=null where id=?",
userServer
);
} else {
conn.executeUpdate(
"update linux.\"UserServer\" set junk_email_retention=? where id=?",
days,
userServer
);
}
invalidateList.addTable(
conn,
Table.TableID.LINUX_SERVER_ACCOUNTS,
getAccountForUserServer(conn, userServer),
getServerForUserServer(conn, userServer),
false
);
}
public static void setUserServerSpamAssassinIntegrationMode(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int userServer,
String mode
) throws IOException, SQLException {
// Security checks
checkAccessUserServer(conn, source, "setUserServerSpamAssassinIntegrationMode", userServer);
com.aoindustries.aoserv.client.linux.User.Name user = getUserForUserServer(conn, userServer);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to set the spam assassin integration mode for User named '"+User.MAIL+'\'');
// Update the database
conn.executeUpdate(
"update linux.\"UserServer\" set sa_integration_mode=? where id=?",
mode,
userServer
);
invalidateList.addTable(
conn,
Table.TableID.LINUX_SERVER_ACCOUNTS,
getAccountForUserServer(conn, userServer),
getServerForUserServer(conn, userServer),
false
);
}
public static void setUserServerSpamAssassinRequiredScore(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int userServer,
float requiredScore
) throws IOException, SQLException {
// Security checks
checkAccessUserServer(conn, source, "setUserServerSpamAssassinRequiredScore", userServer);
com.aoindustries.aoserv.client.linux.User.Name user = getUserForUserServer(conn, userServer);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to set the spam assassin required score for User named '"+User.MAIL+'\'');
// Update the database
conn.executeUpdate(
"update linux.\"UserServer\" set sa_required_score=? where id=?",
requiredScore,
userServer
);
invalidateList.addTable(
conn,
Table.TableID.LINUX_SERVER_ACCOUNTS,
getAccountForUserServer(conn, userServer),
getServerForUserServer(conn, userServer),
false
);
}
public static void setUserServerSpamAssassinDiscardScore(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int userServer,
int discardScore
) throws IOException, SQLException {
// Security checks
checkAccessUserServer(conn, source, "setUserServerSpamAssassinDiscardScore", userServer);
com.aoindustries.aoserv.client.linux.User.Name user = getUserForUserServer(conn, userServer);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to set the spam assassin discard score for User named '"+User.MAIL+'\'');
// Update the database
if(discardScore==-1) {
conn.executeUpdate(
"update linux.\"UserServer\" set sa_discard_score=null where id=?",
userServer
);
} else {
conn.executeUpdate(
"update linux.\"UserServer\" set sa_discard_score=? where id=?",
discardScore,
userServer
);
}
invalidateList.addTable(
conn,
Table.TableID.LINUX_SERVER_ACCOUNTS,
getAccountForUserServer(conn, userServer),
getServerForUserServer(conn, userServer),
false
);
}
public static void setUserServerTrashEmailRetention(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int userServer,
int days
) throws IOException, SQLException {
// Security checks
checkAccessUserServer(conn, source, "setUserServerTrashEmailRetention", userServer);
com.aoindustries.aoserv.client.linux.User.Name user = getUserForUserServer(conn, userServer);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to set the trash email retention for User named '"+User.MAIL+'\'');
// Update the database
if(days==-1) {
conn.executeUpdate(
"update linux.\"UserServer\" set trash_email_retention=null where id=?",
userServer
);
} else {
conn.executeUpdate(
"update linux.\"UserServer\" set trash_email_retention=? where id=?",
days,
userServer
);
}
invalidateList.addTable(
conn,
Table.TableID.LINUX_SERVER_ACCOUNTS,
getAccountForUserServer(conn, userServer),
getServerForUserServer(conn, userServer),
false
);
}
public static void setUserServerUseInbox(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int userServer,
boolean useInbox
) throws IOException, SQLException {
// Security checks
checkAccessUserServer(conn, source, "setUserServerUseInbox", userServer);
com.aoindustries.aoserv.client.linux.User.Name user = getUserForUserServer(conn, userServer);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to set the use_inbox flag for User named '"+User.MAIL+'\'');
// Update the database
conn.executeUpdate(
"update linux.\"UserServer\" set use_inbox=? where id=?",
useInbox,
userServer
);
invalidateList.addTable(
conn,
Table.TableID.LINUX_SERVER_ACCOUNTS,
getAccountForUserServer(conn, userServer),
getServerForUserServer(conn, userServer),
false
);
}
/**
* Waits for any pending or processing account rebuild to complete.
*/
public static void waitForUserRebuild(
DatabaseConnection conn,
RequestSource source,
int linuxServer
) throws IOException, SQLException {
NetHostHandler.checkAccessHost(conn, source, "waitForLinuxAccountRebuild", linuxServer);
NetHostHandler.waitForInvalidates(linuxServer);
AOServDaemonConnector daemonConnector = DaemonHandler.getDaemonConnector(conn, linuxServer);
conn.releaseConnection();
daemonConnector.waitForLinuxAccountRebuild();
}
static boolean canGroupAccessServer(DatabaseConnection conn, RequestSource source, Group.Name group, int linuxServer) throws IOException, SQLException {
return conn.executeBooleanQuery(
"select\n"
+ " (\n"
+ " select\n"
+ " lg.name\n"
+ " from\n"
+ " linux.\"Group\" lg,\n"
+ " billing.\"Package\" pk,\n"
+ " account.\"AccountHost\" bs\n"
+ " where\n"
+ " lg.name=?\n"
+ " and lg.package=pk.name\n"
+ " and pk.accounting=bs.accounting\n"
+ " and bs.server=?\n"
+ " limit 1\n"
+ " )\n"
+ " is not null\n",
group,
linuxServer
);
}
static void checkGroupAccessServer(DatabaseConnection conn, RequestSource source, String action, Group.Name group, int linuxServer) throws IOException, SQLException {
if(!canGroupAccessServer(conn, source, group, linuxServer)) {
throw new SQLException(
"group="
+ group
+ " is not allowed to access server="
+ linuxServer
+ ": action='"
+ action
+ "'"
);
}
}
public static Account.Name getAccountForGroup(DatabaseConnection conn, Group.Name name) throws IOException, SQLException {
return conn.executeObjectQuery(ObjectFactories.accountNameFactory,
"select pk.accounting from linux.\"Group\" lg, billing.\"Package\" pk where lg.package=pk.name and lg.name=?",
name
);
}
// TODO: Is this still relevant?
public static List getAccountsForGroupUser(DatabaseConnection conn, int groupUser) throws IOException, SQLException {
return conn.executeObjectCollectionQuery(new ArrayList<>(),
ObjectFactories.accountNameFactory,
"select\n"
+ " pk1.accounting\n"
+ "from\n"
+ " linux.\"GroupUser\" lga1\n"
+ " inner join linux.\"Group\" lg1 on lga1.\"group\"=lg1.name\n"
+ " inner join billing.\"Package\" pk1 on lg1.package=pk1.name\n"
+ "where\n"
+ " lga1.id=?\n"
+ "union select\n"
+ " pk2.accounting\n"
+ "from\n"
+ " linux.\"GroupUser\" lga2\n"
+ " inner join account.\"User\" un2 on lga2.\"user\" = un2.username\n"
+ " inner join billing.\"Package\" pk2 on un2.package=pk2.name\n"
+ "where\n"
+ " lga2.id=?",
groupUser,
groupUser
);
}
public static Account.Name getAccountForUserServer(DatabaseConnection conn, int userServer) throws IOException, SQLException {
return conn.executeObjectQuery(ObjectFactories.accountNameFactory,
"select\n"
+ " pk.accounting\n"
+ "from\n"
+ " linux.\"UserServer\" lsa,\n"
+ " account.\"User\" un,\n"
+ " billing.\"Package\" pk\n"
+ "where\n"
+ " lsa.id=?\n"
+ " and lsa.username=un.username\n"
+ " and un.package=pk.name",
userServer
);
}
public static Account.Name getAccountForGroupServer(DatabaseConnection conn, int groupServer) throws IOException, SQLException {
return conn.executeObjectQuery(ObjectFactories.accountNameFactory,
"select\n"
+ " pk.accounting\n"
+ "from\n"
+ " linux.\"GroupServer\" lsg,\n"
+ " linux.\"Group\" lg,\n"
+ " billing.\"Package\" pk\n"
+ "where\n"
+ " lsg.id=?\n"
+ " and lsg.name=lg.name\n"
+ " and lg.package=pk.name",
groupServer
);
}
public static Group.Name getGroupForGroupServer(DatabaseConnection conn, int groupServer) throws IOException, SQLException {
return conn.executeObjectQuery(
ObjectFactories.groupNameFactory,
"select name from linux.\"GroupServer\" where id=?",
groupServer
);
}
public static int getServerForUserServer(DatabaseConnection conn, int userServer) throws IOException, SQLException {
return conn.executeIntQuery("select ao_server from linux.\"UserServer\" where id=?", userServer);
}
public static int getServerForGroupServer(DatabaseConnection conn, int groupServer) throws IOException, SQLException {
return conn.executeIntQuery("select ao_server from linux.\"GroupServer\" where id=?", groupServer);
}
public static IntList getServersForUser(DatabaseConnection conn, com.aoindustries.aoserv.client.linux.User.Name user) throws IOException, SQLException {
return conn.executeIntListQuery("select ao_server from linux.\"UserServer\" where username=?", user);
}
public static IntList getServersForGroup(DatabaseConnection conn, Group.Name group) throws IOException, SQLException {
return conn.executeIntListQuery("select ao_server from linux.\"GroupServer\" where name=?", group);
}
public static IntList getServersForGroupUser(DatabaseConnection conn, int groupUser) throws IOException, SQLException {
return conn.executeIntListQuery(
"select\n"
+ " lsg.ao_server\n"
+ "from\n"
+ " linux.\"GroupUser\" lga\n"
+ " inner join linux.\"GroupServer\" lsg on lga.\"group\"=lsg.name\n"
+ " inner join linux.\"UserServer\" lsa on lga.\"user\"=lsa.username\n"
+ " inner join net.\"Host\" se on lsg.ao_server=se.id\n"
+ "where\n"
+ " lga.id=?\n"
+ " and lsg.ao_server=lsa.ao_server\n"
+ " and (\n"
+ " lga.\"operatingSystemVersion\" is null\n"
+ " or lga.\"operatingSystemVersion\" = se.operating_system_version\n"
+ " )",
groupUser
);
}
public static String getTypeForUser(DatabaseConnection conn, com.aoindustries.aoserv.client.linux.User.Name user) throws IOException, SQLException {
return conn.executeStringQuery("select type from linux.\"User\" where username=?", user);
}
public static String getTypeForUserServer(DatabaseConnection conn, int userServer) throws IOException, SQLException {
return conn.executeStringQuery(
"select\n"
+ " la.type\n"
+ "from\n"
+ " linux.\"UserServer\" lsa,\n"
+ " linux.\"User\" la\n"
+ "where\n"
+ " lsa.id=?\n"
+ " and lsa.username=la.username",
userServer
);
}
public static String getTypeForGroupServer(DatabaseConnection conn, int groupServer) throws IOException, SQLException {
return conn.executeStringQuery(
"select\n"
+ " lg.type\n"
+ "from\n"
+ " linux.\"GroupServer\" lsg,\n"
+ " linux.\"Group\" lg\n"
+ "where\n"
+ " lsg.id=?\n"
+ " and lsg.name=lg.name",
groupServer
);
}
public static int getUserServer(DatabaseConnection conn, com.aoindustries.aoserv.client.linux.User.Name user, int linuxServer) throws IOException, SQLException {
int userServer = conn.executeIntQuery(
"select coalesce(\n"
+ " (\n"
+ " select\n"
+ " id\n"
+ " from\n"
+ " linux.\"UserServer\"\n"
+ " where\n"
+ " username=?\n"
+ " and ao_server=?\n"
+ " ), -1\n"
+ ")",
user,
linuxServer
);
if(userServer == -1) throw new SQLException("Unable to find UserServer for " + user + " on " + linuxServer);
return userServer;
}
public static IntList getUserServersForUser(DatabaseConnection conn, com.aoindustries.aoserv.client.linux.User.Name user) throws IOException, SQLException {
return conn.executeIntListQuery("select id from linux.\"UserServer\" where username=?", user);
}
public static IntList getGroupServersForGroup(DatabaseConnection conn, Group.Name group) throws IOException, SQLException {
return conn.executeIntListQuery("select id from linux.\"GroupServer\" where name=?", group);
}
public static Account.Name getPackageForGroup(DatabaseConnection conn, Group.Name group) throws IOException, SQLException {
return conn.executeObjectQuery(ObjectFactories.accountNameFactory,
"select\n"
+ " package\n"
+ "from\n"
+ " linux.\"Group\"\n"
+ "where\n"
+ " name=?",
group
);
}
public static Account.Name getPackageForGroupServer(DatabaseConnection conn, int groupServer) throws IOException, SQLException {
return conn.executeObjectQuery(ObjectFactories.accountNameFactory,
"select\n"
+ " lg.package\n"
+ "from\n"
+ " linux.\"GroupServer\" lsg,\n"
+ " linux.\"Group\" lg\n"
+ "where\n"
+ " lsg.id=?\n"
+ " and lsg.name=lg.name",
groupServer
);
}
public static int getGidForGroupServer(DatabaseConnection conn, int groupServer) throws IOException, SQLException {
return conn.executeIntQuery("select gid from linux.\"GroupServer\" where id=?", groupServer);
}
public static int getUidForUserServer(DatabaseConnection conn, int userServer) throws IOException, SQLException {
return conn.executeIntQuery("select uid from linux.\"UserServer\" where id=?", userServer);
}
public static com.aoindustries.aoserv.client.linux.User.Name getUserForGroupUser(DatabaseConnection conn, int groupUser) throws IOException, SQLException {
return conn.executeObjectQuery(
ObjectFactories.linuxUserNameFactory,
"select \"user\" from linux.\"GroupUser\" where id=?",
groupUser
);
}
public static Group.Name getGroupForGroupUser(DatabaseConnection conn, int groupUser) throws IOException, SQLException {
return conn.executeObjectQuery(ObjectFactories.groupNameFactory,
"select \"group\" from linux.\"GroupUser\" where id=?",
groupUser
);
}
public static com.aoindustries.aoserv.client.linux.User.Name getUserForUserServer(DatabaseConnection conn, int userServer) throws IOException, SQLException {
return conn.executeObjectQuery(
ObjectFactories.linuxUserNameFactory,
"select username from linux.\"UserServer\" where id=?",
userServer
);
}
public static boolean comparePassword(
DatabaseConnection conn,
RequestSource source,
int userServer,
String password
) throws IOException, SQLException {
checkAccessUserServer(conn, source, "comparePassword", userServer);
if(isUserServerDisabled(conn, userServer)) throw new SQLException("Unable to compare password, UserServer disabled: "+userServer);
com.aoindustries.aoserv.client.linux.User.Name user = getUserForUserServer(conn, userServer);
if(user.equals(User.MAIL)) throw new SQLException("Not allowed to compare password for UserServer named '"+User.MAIL+"': "+userServer);
String type=conn.executeStringQuery("select type from linux.\"User\" where username=?", user);
// Make sure passwords can be set before doing a comparison
if(!UserType.canSetPassword(type)) throw new SQLException("Passwords may not be compared for UserType="+type);
// Perform the password comparison
AOServDaemonConnector daemonConnector = DaemonHandler.getDaemonConnector(conn,
getServerForUserServer(conn, userServer)
);
conn.releaseConnection();
return daemonConnector.compareLinuxAccountPassword(user, password);
}
public static void setPrimaryGroupUser(
DatabaseConnection conn,
RequestSource source,
InvalidateList invalidateList,
int groupUser
) throws IOException, SQLException {
checkAccessGroupUser(conn, source, "setPrimaryGroupUser", groupUser);
com.aoindustries.aoserv.client.linux.User.Name user = conn.executeObjectQuery(
ObjectFactories.linuxUserNameFactory,
"select \"user\" from linux.\"GroupUser\" where id=?",
groupUser
);
if(isUserDisabled(conn, user)) throw new SQLException("Unable to set primary GroupUser, User disabled: "+user);
Group.Name group = conn.executeObjectQuery(ObjectFactories.groupNameFactory,
"select \"group\" from linux.\"GroupUser\" where id=?",
groupUser
);
conn.executeUpdate(
"update linux.\"GroupUser\" set \"isPrimary\" = true where id = ?",
groupUser
);
conn.executeUpdate(
"update linux.\"GroupUser\" set \"isPrimary\" = false where \"isPrimary\" and id != ? and \"user\" = ?",
groupUser,
user
);
// Notify all clients of the update
invalidateList.addTable(conn,
Table.TableID.LINUX_GROUP_ACCOUNTS,
InvalidateList.getAccountCollection(AccountUserHandler.getAccountForUser(conn, user), getAccountForGroup(conn, group)),
getServersForGroupUser(conn, groupUser),
false
);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy