key = PublicKeyLoader::load(file_get_contents('/path/to/private/key')); # arrays to hold errors and/or success $this->errors = []; $this->success = []; } /** * Connect to remote FTP server. * Port is 22 and Timeout is 30 seconds. */ private function connectSSH() { $this->ssh = new SSH2('my.domain.com', 22, 30); if (!$this->ssh->login('api', $this->key)) { throw new Exception('Unable to establish SSH connection.'); } } /** * Verify we have a valid username. * Valid usernames may only include (a-z) and dashes (-) * * @param string $username * @return bool */ private function isValidUsername(string $username) { if(preg_match('/[^a-z-]/i', $username)) { return false; } return true; } /** * Generate a cryptographically strong password. * Resulting password length will be twice the length of * the supplied length, and only include [0-9a-z] * * @return string */ private function genPassword(int $length = 8) { return bin2hex(openssl_random_pseudo_bytes($length)); } private function parseResult(string $stdout) { # explode our stdout into individual lines $lines = explode("\n", $stdout); # iterate over the output lines foreach($lines as $line) { # capture any errors if (strpos($line, 'ERROR:') !== false) { $this->errors[] = $line; } if (strpos($line, 'SUCCESS:') !== false) { $this->success[] = $line; } } # if our success array is not empty # (meaning a success line was found) # then our command was successful if(!empty($this->success)) { return true; } return false; } public function addUser(string $username) { # dont allow invalid usernames if(!$this->isValidUsername($username)) { throw new Exception('Invalid username. Only lowercase a-z characters allowed.'); } # generate a new password for the user # note: only admins with local access to the FTP server # may define a password manually $password = $this->genPassword(); # connect to FTP server using SSH $this->connectSSH(); # execute our remote user management script $create = $this->ssh->exec("sudo /etc/vsftpd/users add {$username} {$password}"); # $create will be stdout, so we'll need to parse it to # confirm if the action was successful $result = $this->parseResult($create); # return the result return [ 'success' => $result, 'message' => $result ? $this->success[0] : $this->errors[0], 'password' => $result ? $password : null ]; } public function delUser(string $username) { # dont allow invalid usernames if(!$this->isValidUsername($username)) { throw new Exception('Invalid username. Only lowercase a-z characters allowed.'); } # connect to FTP server using SSH $this->connectSSH(); # execute our remote user management script $delete = $this->ssh->exec("sudo /etc/vsftpd/users del {$username}"); # $delete will be stdout, so we'll need to parse it to # confirm if the action was successful $result = $this->parseResult($delete); # return the result return [ 'success' => $result, 'message' => $result ? $this->success[0] : $this->errors[0] ]; } public function editUser(string $username) { # dont allow invalid usernames if(!$this->isValidUsername($username)) { throw new Exception('Invalid username. Only lowercase a-z characters allowed.'); } # generate a new password for the user # note: only admins with local access to the FTP server # may define a password manually $password = $this->genPassword(); # connect to FTP server using SSH $this->connectSSH(); # execute our remote user management script $edit = $this->ssh->exec("sudo /etc/vsftpd/users edit {$username} {$password}"); # $edit will be stdout, so we'll need to parse it to # confirm if the action was successful $result = $this->parseResult($edit); # return the result return [ 'success' => $result, 'message' => $result ? $this->success[0] : $this->errors[0], 'password' => $result ? $password : null ]; } }