All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aoindustries.aoserv.client.linux.User Maven / Gradle / Ivy

There is a newer version: 1.92.0
Show newest version
/*
 * aoserv-client - Java client for the AOServ Platform.
 * Copyright (C) 2000-2013, 2016, 2017, 2018, 2019, 2020  AO Industries, Inc.
 *     [email protected]
 *     7262 Bull Pen Cir
 *     Mobile, AL 36695
 *
 * This file is part of aoserv-client.
 *
 * aoserv-client 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-client 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-client.  If not, see .
 */
package com.aoindustries.aoserv.client.linux;

import com.aoindustries.aoserv.client.CannotRemoveReason;
import com.aoindustries.aoserv.client.Disablable;
import com.aoindustries.aoserv.client.Removable;
import com.aoindustries.aoserv.client.account.DisableLog;
import com.aoindustries.aoserv.client.ftp.GuestUser;
import static com.aoindustries.aoserv.client.linux.ApplicationResources.accessor;
import com.aoindustries.aoserv.client.password.PasswordChecker;
import com.aoindustries.aoserv.client.password.PasswordProtected;
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.dto.DtoFactory;
import com.aoindustries.io.FastExternalizable;
import com.aoindustries.io.stream.StreamableInput;
import com.aoindustries.io.stream.StreamableOutput;
import com.aoindustries.net.Email;
import com.aoindustries.sql.UnmodifiableTimestamp;
import com.aoindustries.util.ComparatorUtils;
import com.aoindustries.util.Internable;
import com.aoindustries.validation.InvalidResult;
import com.aoindustries.validation.ValidResult;
import com.aoindustries.validation.ValidationException;
import com.aoindustries.validation.ValidationResult;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectInputValidation;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * One user may have shell, FTP, and/or email access to any number
 * of servers.  However, some of the information is common across
 * all machines, and that set of information is contained in a
 * LinuxAccount.
 *
 * @author  AO Industries, Inc.
 */
final public class User extends CachedObjectUserNameKey implements PasswordProtected, Removable, Disablable {

	/**
	 * Used for the various user-provided fields in the /etc/passwd file.
	 * 

* https://wikipedia.org/wiki/Gecos_field *

* * @author AO Industries, Inc. */ final public static class Gecos implements Comparable, Serializable, ObjectInputValidation, DtoFactory, Internable { private static final long serialVersionUID = -117164942375352467L; public static final int MAX_LENGTH = 100; /** * Determines if a name can be used as a GECOS field. A GECOS field * is valid if it is between 1 and 100 characters in length and uses only * {@code [a-z,A-Z,0-9,-,_,@, ,.,#,=,/,$,%,^,&,*,(,),?,']} for each * character.
*
* Refer to man 5 passwd */ public static ValidationResult validate(String value) { // Be non-null if(value==null) return new InvalidResult(accessor, "User.Gecos.validate.isNull"); int len = value.length(); if(len==0) return new InvalidResult(accessor, "User.Gecos.validate.isEmpty"); if(len>MAX_LENGTH) return new InvalidResult(accessor, "User.Gecos.validate.tooLong", MAX_LENGTH, len); for (int c = 0; c < len; c++) { char ch = value.charAt(c); if ( (ch < 'a' || ch > 'z') && (ch<'A' || ch>'Z') && (ch < '0' || ch > '9') && ch != '-' && ch != '_' && ch != '@' && ch != ' ' && ch != '.' && ch != '#' && ch != '=' && ch != '/' && ch != '$' && ch != '%' && ch != '^' && ch != '&' && ch != '*' && ch != '(' && ch != ')' && ch != '?' && ch != '\'' && ch != '+' ) return new InvalidResult(accessor, "User.Gecos.validate.invalidCharacter", ch); } return ValidResult.getInstance(); } private static final ConcurrentMap interned = new ConcurrentHashMap<>(); /** * @param value when {@code null}, returns {@code null} */ public static Gecos valueOf(String value) throws ValidationException { if(value == null) return null; //Gecos existing = interned.get(value); //return existing!=null ? existing : new Gecos(value); return new Gecos(value, true); } final private String value; private Gecos(String value, boolean validate) throws ValidationException { this.value = value; if(validate) validate(); } /** * @param value Does not validate, should only be used with a known valid value. */ private Gecos(String value) { ValidationResult result; assert (result = validate(value)).isValid() : result.toString(); this.value = value; } private void validate() throws ValidationException { ValidationResult result = validate(value); if(!result.isValid()) throw new ValidationException(result); } /** * Perform same validation as constructor on readObject. */ private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { ois.defaultReadObject(); validateObject(); } @Override public void validateObject() throws InvalidObjectException { try { validate(); } catch(ValidationException err) { InvalidObjectException newErr = new InvalidObjectException(err.getMessage()); newErr.initCause(err); throw newErr; } } @Override public boolean equals(Object O) { return O!=null && O instanceof Gecos && value.equals(((Gecos)O).value) ; } @Override public int hashCode() { return value.hashCode(); } @Override public int compareTo(Gecos other) { return this==other ? 0 : ComparatorUtils.compareIgnoreCaseConsistentWithEquals(value, other.value); } @Override public String toString() { return value; } /** * Interns this id much in the same fashion as String.intern(). * * @see String#intern() */ @Override public Gecos intern() { Gecos existing = interned.get(value); if(existing==null) { String internedValue = value.intern(); Gecos addMe = (value == internedValue) ? this : new Gecos(internedValue); // Using identity String comparison to see if already interned existing = interned.putIfAbsent(internedValue, addMe); if(existing==null) existing = addMe; } return existing; } @Override public com.aoindustries.aoserv.client.dto.Gecos getDto() { return new com.aoindustries.aoserv.client.dto.Gecos(value); } } /** * Represents a Linux username. {@link User} names must: *
    *
  • Be non-null
  • *
  • Be non-empty
  • *
  • Be between 1 and 32 characters
  • *
  • Must start with [a-z]
  • *
  • Uses only ASCII 0x21 through 0x7f, excluding {@code space , : ( ) [ ] ' " | & ; A-Z \ /}
  • *
  • * If contains any @ symbol, must also be a valid email address. Please note that the * reverse is not implied - email addresses may exist that are not valid user names. *
  • *
  • May not start with cyrus@
  • *
  • TODO: May only end on "$"?
  • *
  • TODO: "+" is allowed, "lost+found" should be specifically disallowed due to /home/lost+found on mount points.
  • *
  • Must be a valid {@link com.aoindustries.aoserv.client.account.User.Name} - this is implied by the above rules
  • *
* * @see Email#validate(java.lang.String) * * @author AO Industries, Inc. */ // TODO: Update for IEEE Std 1003.1.2001 "3.426 User Name"? https://paulgorman.org/technical/presentations/linux_username_conventions.pdf // TODO: Rename "LinuxName" and combined with "GroupName" as "PosixName" (and an associated "PosixPortableFilename")? public static class Name extends com.aoindustries.aoserv.client.account.User.Name implements FastExternalizable, ObjectInputValidation { /** * The maximum length of a Linux username. *

* Implementation Note:
* 32 characters *

*/ public static final int MAX_LENGTH = 32; /** * Validates a {@link User} name. */ public static ValidationResult validate(String name) { if(name==null) return new InvalidResult(accessor, "User.Name.validate.isNull"); int len = name.length(); if(len==0) return new InvalidResult(accessor, "User.Name.validate.isEmpty"); if(len > MAX_LENGTH) return new InvalidResult(accessor, "User.Name.validate.tooLong", MAX_LENGTH, len); // The first character must be [a-z] char ch = name.charAt(0); if(ch < 'a' || ch > 'z') return new InvalidResult(accessor, "User.Name.validate.startAToZ"); // The rest may have additional characters boolean hasAt = false; for (int c = 1; c < len; c++) { ch = name.charAt(c); if(ch==' ') return new InvalidResult(accessor, "User.Name.validate.noSpace"); if(ch<=0x21 || ch>0x7f) return new InvalidResult(accessor, "User.Name.validate.specialCharacter"); if(ch>='A' && ch<='Z') return new InvalidResult(accessor, "User.Name.validate.noCapital"); switch(ch) { case ',' : return new InvalidResult(accessor, "User.Name.validate.comma"); case ':' : return new InvalidResult(accessor, "User.Name.validate.colon"); case '(' : return new InvalidResult(accessor, "User.Name.validate.leftParen"); case ')' : return new InvalidResult(accessor, "User.Name.validate.rightParen"); case '[' : return new InvalidResult(accessor, "User.Name.validate.leftSquare"); case ']' : return new InvalidResult(accessor, "User.Name.validate.rightSquare"); case '\'' : return new InvalidResult(accessor, "User.Name.validate.apostrophe"); case '"' : return new InvalidResult(accessor, "User.Name.validate.quote"); case '|' : return new InvalidResult(accessor, "User.Name.validate.verticalBar"); case '&' : return new InvalidResult(accessor, "User.Name.validate.ampersand"); case ';' : return new InvalidResult(accessor, "User.Name.validate.semicolon"); case '\\' : return new InvalidResult(accessor, "User.Name.validate.backslash"); case '/' : return new InvalidResult(accessor, "User.Name.validate.slash"); case '@' : hasAt = true; break; } } // More strict at sign control is required for user@domain structure in Cyrus virtdomains. if(hasAt) { // Must also be a valid email address ValidationResult result = Email.validate(name); if(!result.isValid()) return result; if(name.startsWith("cyrus@")) return new InvalidResult(accessor, "User.Name.validate.startWithCyrusAt"); } assert com.aoindustries.aoserv.client.account.User.Name.validate(name).isValid() : "A Linux User.Name is always a valid Account User.Name."; return ValidResult.getInstance(); } private static final ConcurrentMap interned = new ConcurrentHashMap<>(); /** * @param name when {@code null}, returns {@code null} */ public static Name valueOf(String name) throws ValidationException { if(name == null) return null; //Name existing = interned.get(name); //return existing!=null ? existing : new Name(name); return new Name(name, true); } protected Name(String name, boolean validate) throws ValidationException { super(name, validate); } /** * @param name Does not validate, should only be used with a known valid value. */ protected Name(String name) { super(name); } @Override protected void validate() throws ValidationException { ValidationResult result = validate(name); if(!result.isValid()) throw new ValidationException(result); } /** * {@inheritDoc} */ @Override public Name intern() { Name existing = interned.get(name); if(existing==null) { String internedId = name.intern(); Name addMe = (name == internedId) ? this : new Name(internedId); existing = interned.putIfAbsent(internedId, addMe); if(existing==null) existing = addMe; } return existing; } @Override public com.aoindustries.aoserv.client.dto.LinuxUserName getDto() { return new com.aoindustries.aoserv.client.dto.LinuxUserName(name); } // private static final long serialVersionUID = 2L; public Name() { } @Override public long getSerialVersionUID() { return serialVersionUID; } // } static final int COLUMN_USERNAME=0; static final String COLUMN_USERNAME_name = "username"; /** * Some commonly used system and application account usernames. */ public static final Name ADM, AOADMIN, AOSERV_JILTER, AOSERV_XEN_MIGRATION, APACHE, AVAHI_AUTOIPD, AWSTATS, BIN, BIRD, CENTOS, // Amazon EC2 cloud-init CHRONY, CLAMSCAN, CLAMUPDATE, CYRUS, DAEMON, DBUS, DHCPD, EMAILMON, FTP, FTPMON, GAMES, HALT, INTERBASE, LP, MAIL, MAILNULL, MEMCACHED, MYSQL, NAMED, NFSNOBODY, NGINX, NOBODY, OPERATOR, POLKITD, POSTGRES, REDIS, ROOT, RPC, RPCUSER, SASLAUTH, SHUTDOWN, SMMSP, SSHD, SYNC, SYSTEMD_BUS_PROXY, SYSTEMD_NETWORK, TCPDUMP, TSS, UNBOUND, // AOServ Master: AOSERV_MASTER, // AOServ Schema: ACCOUNTING, BILLING, DISTRIBUTION, INFRASTRUCTURE, MANAGEMENT, MONITORING, RESELLER; /** * @deprecated User httpd no longer used. */ @Deprecated public static final Name HTTPD; static { try { ADM = Name.valueOf("adm"); AOADMIN = Name.valueOf("aoadmin"); AOSERV_JILTER = Name.valueOf("aoserv-jilter"); AOSERV_XEN_MIGRATION = Name.valueOf("aoserv-xen-migration"); APACHE = Name.valueOf("apache"); AVAHI_AUTOIPD = Name.valueOf("avahi-autoipd"); AWSTATS = Name.valueOf("awstats"); BIN = Name.valueOf("bin"); BIRD = Name.valueOf("bird"); CENTOS = Name.valueOf("centos"); CHRONY = Name.valueOf("chrony"); CLAMSCAN = Name.valueOf("clamscan"); CLAMUPDATE = Name.valueOf("clamupdate"); CYRUS = Name.valueOf("cyrus"); DAEMON = Name.valueOf("daemon"); DBUS = Name.valueOf("dbus"); DHCPD = Name.valueOf("dhcpd"); EMAILMON = Name.valueOf("emailmon"); FTP = Name.valueOf("ftp"); FTPMON = Name.valueOf("ftpmon"); GAMES = Name.valueOf("games"); HALT = Name.valueOf("halt"); INTERBASE = Name.valueOf("interbase"); LP = Name.valueOf("lp"); MAIL = Name.valueOf("mail"); MAILNULL = Name.valueOf("mailnull"); MEMCACHED = Name.valueOf("memcached"); MYSQL = Name.valueOf("mysql"); NAMED = Name.valueOf("named"); NFSNOBODY = Name.valueOf("nfsnobody"); NGINX = Name.valueOf("nginx"); NOBODY = Name.valueOf("nobody"); OPERATOR = Name.valueOf("operator"); POLKITD = Name.valueOf("polkitd"); POSTGRES = Name.valueOf("postgres"); REDIS = Name.valueOf("redis"); ROOT = Name.valueOf("root"); RPC = Name.valueOf("rpc"); RPCUSER = Name.valueOf("rpcuser"); SASLAUTH = Name.valueOf("saslauth"); SHUTDOWN = Name.valueOf("shutdown"); SMMSP = Name.valueOf("smmsp"); SSHD = Name.valueOf("sshd"); SYNC = Name.valueOf("sync"); SYSTEMD_BUS_PROXY = Name.valueOf("systemd-bus-proxy"); SYSTEMD_NETWORK = Name.valueOf("systemd-network"); TCPDUMP = Name.valueOf("tcpdump"); TSS = Name.valueOf("tss"); UNBOUND = Name.valueOf("unbound"); // Now unused HTTPD = Name.valueOf("httpd"); // AOServ Master: AOSERV_MASTER = Name.valueOf("aoserv-master"); // AOServ Schema: ACCOUNTING = Name.valueOf("accounting"); BILLING = Name.valueOf("billing"); DISTRIBUTION = Name.valueOf("distribution"); INFRASTRUCTURE = Name.valueOf("infrastructure"); MANAGEMENT = Name.valueOf("management"); MONITORING = Name.valueOf("monitoring"); RESELLER = Name.valueOf("reseller"); } catch(ValidationException e) { throw new AssertionError("These hard-coded values are valid", e); } } public static final String NO_PASSWORD_CONFIG_VALUE="!!"; private Gecos name; private Gecos office_location; private Gecos office_phone; private Gecos home_phone; private String type; private PosixPath shell; private UnmodifiableTimestamp created; int disable_log; public void addFTPGuestUser() throws IOException, SQLException { table.getConnector().getFtp().getGuestUser().addFTPGuestUser(pkey); } public void addLinuxGroup(Group group) throws IOException, SQLException { table.getConnector().getLinux().getGroupUser().addLinuxGroupAccount(group, this); } public int addLinuxServerAccount(Server aoServer, PosixPath home) throws IOException, SQLException { return table.getConnector().getLinux().getUserServer().addLinuxServerAccount(this, aoServer, home); } @Override public int arePasswordsSet() throws IOException, SQLException { return com.aoindustries.aoserv.client.account.User.groupPasswordsSet(getLinuxServerAccounts()); } @Override public boolean canDisable() throws IOException, SQLException { // Already disabled if(disable_log!=-1) return false; // linux_server_accounts for(UserServer lsa : getLinuxServerAccounts()) if(!lsa.isDisabled()) return false; return true; } @Override public boolean canEnable() throws SQLException, IOException { DisableLog dl=getDisableLog(); if(dl==null) return false; else return dl.canEnable() && !getUsername().isDisabled(); } @Override public List checkPassword(String password) throws IOException { return checkPassword(pkey, type, password); } /** * Checks the strength of a password as required for this * LinuxAccount. The strength requirement * depends on the LinuxAccountType. * * @see UserType#getPasswordStrength(java.lang.String) * @see PasswordChecker#checkPassword(com.aoindustries.aoserv.client.account.User.Name, java.lang.String, com.aoindustries.aoserv.client.password.PasswordChecker.PasswordStrength) */ public static List checkPassword(Name username, String type, String password) throws IOException { return PasswordChecker.checkPassword(username, password, UserType.getPasswordStrength(type)); } @Override public void disable(DisableLog dl) throws IOException, SQLException { table.getConnector().requestUpdateIL(true, AoservProtocol.CommandID.DISABLE, Table.TableID.LINUX_ACCOUNTS, dl.getPkey(), pkey); } @Override public void enable() throws IOException, SQLException { table.getConnector().requestUpdateIL(true, AoservProtocol.CommandID.ENABLE, Table.TableID.LINUX_ACCOUNTS, pkey); } @Override protected Object getColumnImpl(int i) { switch(i) { case COLUMN_USERNAME: return pkey; case 1: return name; case 2: return office_location; case 3: return office_phone; case 4: return home_phone; case 5: return type; case 6: return shell; case 7: return created; case 8: return disable_log==-1?null:disable_log; default: throw new IllegalArgumentException("Invalid index: " + i); } } public UnmodifiableTimestamp getCreated() { return created; } @Override public boolean isDisabled() { return disable_log!=-1; } @Override public DisableLog getDisableLog() throws SQLException, IOException { if(disable_log==-1) return null; DisableLog obj=table.getConnector().getAccount().getDisableLog().get(disable_log); if(obj==null) throw new SQLException("Unable to find DisableLog: "+disable_log); return obj; } public GuestUser getFTPGuestUser() throws IOException, SQLException { return table.getConnector().getFtp().getGuestUser().get(pkey); } public Gecos getHomePhone() { return home_phone; } public List getLinuxGroups() throws IOException, SQLException { return table.getConnector().getLinux().getGroupUser().getLinuxGroups(this); } public UserServer getLinuxServerAccount(Server aoServer) throws IOException, SQLException { return table.getConnector().getLinux().getUserServer().getLinuxServerAccount(aoServer, pkey); } public List getLinuxServerAccounts() throws IOException, SQLException { return table.getConnector().getLinux().getUserServer().getLinuxServerAccounts(this); } public Gecos getName() { return name; } public Gecos getOfficeLocation() { return office_location; } public Gecos getOfficePhone() { return office_phone; } public Group getPrimaryGroup() throws IOException, SQLException { return table.getConnector().getLinux().getGroupUser().getPrimaryGroup(this); } public Shell getShell() throws SQLException, IOException { Shell shellObject = table.getConnector().getLinux().getShell().get(shell); if (shellObject == null) throw new SQLException("Unable to find Shell: " + shell); return shellObject; } @Override public Table.TableID getTableID() { return Table.TableID.LINUX_ACCOUNTS; } public UserType getType() throws IOException, SQLException { UserType typeObject = table.getConnector().getLinux().getUserType().get(type); if (typeObject == null) throw new IllegalArgumentException(new SQLException("Unable to find LinuxAccountType: " + type)); return typeObject; } public Name getUsername_id() { return pkey; } public com.aoindustries.aoserv.client.account.User getUsername() throws SQLException, IOException { com.aoindustries.aoserv.client.account.User usernameObject = table.getConnector().getAccount().getUser().get(pkey); if (usernameObject == null) throw new SQLException("Unable to find Username: " + pkey); return usernameObject; } public List getValidHomeDirectories(Server ao) throws SQLException, IOException { return getValidHomeDirectories(pkey, ao); } public static List getValidHomeDirectories(Name username, Server ao) throws SQLException, IOException { try { List dirs=new ArrayList<>(); if(username != null) { dirs.add(UserServer.getDefaultHomeDirectory(username)); dirs.add(UserServer.getHashedHomeDirectory(username)); } List hss=ao.getHttpdSites(); int hsslen=hss.size(); for(int c=0;c hsts=ao.getHttpdSharedTomcats(); int hstslen=hsts.size(); for(int c=0;c> getCannotRemoveReasons() throws SQLException, IOException { List> reasons=new ArrayList<>(); // All LinuxServerAccounts must be removable for(UserServer lsa : getLinuxServerAccounts()) { reasons.addAll(lsa.getCannotRemoveReasons()); } return reasons; } @Override public void remove() throws IOException, SQLException { table.getConnector().requestUpdateIL( true, AoservProtocol.CommandID.REMOVE, Table.TableID.LINUX_ACCOUNTS, pkey ); } public void removeLinuxGroup(Group group) throws IOException, SQLException { for(GroupUser lga : table.getConnector().getLinux().getGroupUser().getLinuxGroupAccounts(group.getName(), pkey)) { lga.remove(); } } public void setHomePhone(Gecos phone) throws IOException, SQLException { table.getConnector().requestUpdateIL(true, AoservProtocol.CommandID.SET_LINUX_ACCOUNT_HOME_PHONE, pkey, phone==null?"":phone.toString()); } public void setName(Gecos name) throws IOException, SQLException { table.getConnector().requestUpdateIL( true, AoservProtocol.CommandID.SET_LINUX_ACCOUNT_NAME, pkey, name==null ? "" : name.toString() ); } public void setOfficeLocation(Gecos location) throws IOException, SQLException { table.getConnector().requestUpdateIL(true, AoservProtocol.CommandID.SET_LINUX_ACCOUNT_OFFICE_LOCATION, pkey, location==null?"":location.toString()); } public void setOfficePhone(Gecos phone) throws IOException, SQLException { table.getConnector().requestUpdateIL(true, AoservProtocol.CommandID.SET_LINUX_ACCOUNT_OFFICE_PHONE, pkey, phone==null?"":phone.toString()); } @Override public void setPassword(String password) throws SQLException, IOException { for(UserServer lsa : getLinuxServerAccounts()) { if(lsa.canSetPassword()) lsa.setPassword(password); } } public void setShell(Shell shell) throws IOException, SQLException { table.getConnector().requestUpdateIL(true, AoservProtocol.CommandID.SET_LINUX_ACCOUNT_SHELL, pkey, shell.getPath()); } @Override public void write(StreamableOutput out, AoservProtocol.Version protocolVersion) throws IOException { out.writeUTF(pkey.toString()); if(protocolVersion.compareTo(AoservProtocol.Version.VERSION_1_80_1) < 0) { // Older clients require name, use "*" as name when none set out.writeUTF(name==null ? "*" : name.toString()); } else { out.writeNullUTF(Objects.toString(name, null)); } out.writeNullUTF(Objects.toString(office_location, null)); out.writeNullUTF(Objects.toString(office_phone, null)); out.writeNullUTF(Objects.toString(home_phone, null)); out.writeUTF(type); out.writeUTF(shell.toString()); if(protocolVersion.compareTo(AoservProtocol.Version.VERSION_1_83_0) < 0) { out.writeLong(created.getTime()); } else { out.writeTimestamp(created); } out.writeCompressedInt(disable_log); } @Override public boolean canSetPassword() throws IOException, SQLException { return disable_log==-1 && getType().canSetPassword(); } public void setPrimaryLinuxGroup(Group group) throws SQLException, IOException { List lgas = table.getConnector().getLinux().getGroupUser().getLinuxGroupAccounts(group.getName(), pkey); if(lgas.isEmpty()) throw new SQLException("Unable to find LinuxGroupAccount for username="+pkey+" and group="+group.getName()); if(lgas.size() > 1) throw new SQLException("Found more than one LinuxGroupAccount for username="+pkey+" and group="+group.getName()); lgas.get(0).setAsPrimary(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy