From 30c71db6aecf94439ca6ac50f558f3c265301983 Mon Sep 17 00:00:00 2001 From: Luke De Mouy Date: Tue, 12 Jan 2016 19:39:41 -0700 Subject: [PATCH 01/20] Create the UserManager class, and pull in the functionality from the old usermanager backend. Brings in the following functionality: *Create/Delete User *Create/Delete Group *Add/Remove User from Group *Change a Users Password/Shell/Full Name *Get the list of Users, Groups, and Shells --- src/library/library.pro | 8 +- src/library/sysadm-usermanager.cpp | 341 +++++++++++++++++++++++++++++ src/library/sysadm-usermanager.h | 99 +++++++++ 3 files changed, 445 insertions(+), 3 deletions(-) create mode 100644 src/library/sysadm-usermanager.cpp create mode 100644 src/library/sysadm-usermanager.h diff --git a/src/library/library.pro b/src/library/library.pro index 769fca9..c08f0aa 100644 --- a/src/library/library.pro +++ b/src/library/library.pro @@ -14,17 +14,19 @@ HEADERS += sysadm-global.h \ sysadm-general.h \ sysadm-lifepreserver.h \ sysadm-network.h \ - sysadm-firewall.h + sysadm-firewall.h \ + sysadm-usermanager.h SOURCES += sysadm-general.cpp \ sysadm-lifepreserver.cpp \ sysadm-network.cpp \ NetDevice.cpp \ - sysadm-firewall.cpp + sysadm-firewall.cpp \ + sysadm-usermanager.cpp include.path=/usr/local/include/ include.files=sysadm-*.h -INSTALLS += target include +INSTALLS += target include QMAKE_LIBDIR = /usr/local/lib/qt5 /usr/local/lib diff --git a/src/library/sysadm-usermanager.cpp b/src/library/sysadm-usermanager.cpp new file mode 100644 index 0000000..fa3ea6c --- /dev/null +++ b/src/library/sysadm-usermanager.cpp @@ -0,0 +1,341 @@ +//=========================================== +// PC-BSD source code +// Copyright (c) 2015, PC-BSD Software/iXsystems +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "sysadm-usermanager.h" +using namespace sysadm; + +UserManager::UserManager(QString chroot) +{ + loadUsers(); + loadGroups(); + loadShells(); +} + +void UserManager::NewUser(QString fullName, QString userName, QString password, QString home, QString shell, int gid, int uid) +{ + if (home == "/usr/home/") + home += userName; + //Add User + qDebug() << "Adding user " << userName; + // Create the new home-directory + if ( chroot.isEmpty() ) + { + system("/usr/local/share/pcbsd/scripts/mkzfsdir.sh " + home.toLatin1() ); + system("pw groupadd " + userName.toLatin1() ); + } else { + system("mkdir -p " + chroot.toLatin1() + "/" + home.toLatin1() + " 2>/dev/null" ); + system("chroot " + chroot.toLatin1() + " ln -s /usr/home /home 2>/dev/null" ); + system("chroot " + chroot.toLatin1() + " pw groupadd " + userName.toLatin1() ); + } + + QStringList args; + if ( ! chroot.isEmpty() ) + args << chroot << "pw"; + args << "useradd"; + args << userName; + args << "-c"; + args << fullName; + args << "-m"; + args << "-d"; + args << home; + args << "-s"; + args << shell; + if (gid != -1) + { + args << "-g"; + args << QString::number(gid); + } else { + args << "-g"; + args << userName; + } + args << "-G"; + args << "operator"; + if( uid != -1) + { + args << "-u"; + args << QString::number( uid ); + } + if ( ! chroot.isEmpty() ) + QProcess::execute("chroot", args); + else + QProcess::execute("pw", args); + + QTemporaryFile nfile("/tmp/.XXXXXXXX"); + if ( nfile.open() ) + { + QTextStream stream( &nfile ); + stream << password; + nfile.close(); + } + if ( ! chroot.isEmpty() ) + system("cat " + nfile.fileName().toLatin1() + " | chroot " + chroot.toLatin1() + " pw usermod " + userName.toLatin1() + " -h 0 "); + else + system("cat " + nfile.fileName().toLatin1() + " | pw usermod " + userName.toLatin1() + " -h 0 "); + nfile.remove(); + + if ( chroot.isEmpty() ) { + qDebug() << "Enabling Flash Plugin for " << userName; + QString flashCmd = "su " + userName + " -c \"flashpluginctl on\""; + system(flashCmd.toLatin1()); + } + + // Set permissions + if ( chroot.isEmpty() ) + system("chown -R " + userName.toLatin1() +":" + userName.toLatin1() + " " + home.toLatin1() ); + else + system("chroot " + chroot.toLatin1() + " chown -R " + userName.toLatin1() +":" + userName.toLatin1() + " " + home.toLatin1() ); + + //reloads the groups and users so that the internal model is consistent + loadUsers(); + loadGroups(); +} + +void UserManager::DeleteUser(User user) +{ + //Delete User + qDebug() << "Deleting user " << user.UserName; + + /*if(userIt->getEnc()) + { + // Unmount PEFS + system("umount " + userIt->getHome().toLatin1() ); + }*/ + QStringList args; + if ( ! chroot.isEmpty() ) + args << chroot << "pw"; + args << "userdel"; + args << user.UserName; + args << "-r"; + system("/usr/local/share/pcbsd/scripts/rmzfsdir.sh " + user.HomeFolder.toLatin1() ); + + if ( ! chroot.isEmpty() ) + QProcess::execute("chroot", args); + else + QProcess::execute("pw", args); + + //update the internal model + users.removeAll(user); + loadGroups(); +} + +const QVector UserManager::GetUsers() +{ + return users; +} + +const User UserManager::GetUser(int id) +{ + for(User user: users) + { + if(user.ID == id) + return user; + } + return User(); +} + +const User UserManager::GetUser(QString userName) +{ + for(User user: users) + { + if(user.UserName == userName) + return user; + } + return User(); +} + +void UserManager::ChangeUserPassword(User user, QString newPassword) +{ + qDebug() << "Changing password: " << user.UserName; + + // Set the new PW + QTemporaryFile rfile("/tmp/.XXXXXXXX"); + if ( rfile.open() ) { + QTextStream stream( &rfile ); + stream << newPassword; + rfile.close(); + } + if ( ! chroot.isEmpty() ) + system("cat " + rfile.fileName().toLatin1() + " | chroot " + chroot.toLatin1() + " pw usermod " + user.UserName.toLatin1() + " -h 0 "); + else + system("cat " + rfile.fileName().toLatin1() + " | pw usermod " + user.UserName.toLatin1() + " -h 0 "); + + rfile.remove(); + +} + +void UserManager::ChangeUserShell(User user, QString shell) +{ + if(shells.contains(shell)) + system("chsh -s " + shell.toLocal8Bit() + " " + user.UserName.toLocal8Bit()); + else + qDebug("Shell not found"); +} + +void UserManager::ChangeUserFullName(User user, QString newName) +{ + system("pw usermod " + user.UserName.toLatin1() + " -c " + newName.toLatin1()); +} + +void UserManager::AddUserToGroup(User user, Group group) +{ + system("pw groupmod "+ group.Name.toLatin1() + " -m " + user.UserName.toLatin1()); +} + +void UserManager::RemoveUserFromGroup(User user, Group group) +{ + system("pw groupmod"+ group.Name.toLatin1() +"-d" + user.UserName.toLatin1()); +} + +void UserManager::NewGroup(QString name, QStringList members) +{ + QStringList args; + qDebug() << "Adding group " << name; + if ( ! chroot.isEmpty() ) + args << chroot << "pw"; + args << "groupadd"; + args << name; + args << "-M"; + args << members.join(","); + if ( ! chroot.isEmpty() ) + QProcess::execute("chroot", args); + else + QProcess::execute("pw", args); + +} + +void UserManager::DeleteGroup(Group group) +{ + QStringList args; + qDebug() << "Deleting group " << group.Name; + if ( ! chroot.isEmpty() ) + args << chroot << "pw"; + args << "groupdel"; + args << group.Name; + if ( ! chroot.isEmpty() ) + QProcess::execute("chroot", args); + else + QProcess::execute("pw", args); +} + +const QVector UserManager::GetGroups() +{ + return groups; +} + +const Group UserManager::getGroup(int id) +{ + for(Group group : groups) + { + if(group.ID == id) + return group; + } + return Group(); +} + +const Group UserManager::getGroup(QString name) +{ + for(Group group : groups) + { + if(group.Name == name) + return group; + } + return Group(); +} + +const QStringList UserManager::GetShells() +{ + return shells; +} + +void UserManager::loadUsers() +{ + users.clear(); + QFile userFile(chroot + "/etc/passwd"); + if ( userFile.open(QIODevice::ReadOnly) ) + { + QTextStream stream(&userFile); + stream.setCodec("UTF-8"); + QString line; + + while ( !stream.atEnd() ) + { + line = stream.readLine(); + + if ((line.indexOf("#") != 0) && (! line.isEmpty())) //Make sure it isn't a comment or blank + { + User user; + user.UserName = line.section(":",0,0); + user.ID = line.section(":",2,2).toInt(); + user.GroupID = line.section(":",3,3).toInt(); + user.HomeFolder = line.section(":",5,5); + user.Shell = line.section(":",6,6); + user.FullName = line.section(":",4,4); + + users.append(user); + } + } + } else { + //Unable to open file error + qWarning("Error! Unable to open /etc/passwd"); + } +} + +void UserManager::loadGroups() +{ + groups.clear(); + QFile groupFile(chroot + "/etc/group"); + if ( groupFile.open(QIODevice::ReadOnly) ) + { + QTextStream stream(&groupFile); + stream.setCodec("UTF-8"); + + QString line; + + while ( !stream.atEnd() ) + { + line = stream.readLine(); + + if ((line.indexOf("#") != 0) && (! line.isEmpty())) //Make sure it isn't a comment or blank + { + Group group; + group.Name = line.section(":",0,0); + group.ID = line.section(":",2,2).toInt(); + QString memberString = line.section(":",3,3); + group.Users = memberString.split(","); + + groups.append(group); + } + } + } else { + //Unable to open file error + qWarning("Error! Unable to open /etc/group"); + } +} + +void UserManager::loadShells() +{ + QFile shellFile(chroot + "/etc/shells"); + if ( shellFile.open(QIODevice::ReadOnly) ) { + QTextStream stream(&shellFile); + stream.setCodec("UTF-8"); + + QString line; + + while ( !stream.atEnd() ) { + line = stream.readLine(); + + if ( !line.startsWith("#") && !line.isEmpty() ) { //Make sure it isn't a comment or blank + shells.append(line); + } + } + } else { + //Unable to open file error + qWarning("Error! Unable to open /etc/shells"); + } + + // Add /sbin/nologin as well + shells.append("/sbin/nologin"); +} diff --git a/src/library/sysadm-usermanager.h b/src/library/sysadm-usermanager.h new file mode 100644 index 0000000..f7e1932 --- /dev/null +++ b/src/library/sysadm-usermanager.h @@ -0,0 +1,99 @@ +//=========================================== +// PC-BSD source code +// Copyright (c) 2015, PC-BSD Software/iXsystems +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== + +#ifndef USERMANAGER_H +#define USERMANAGER_H +#include + +namespace sysadm{ +class User +{ +public: + QString FullName; + QString UserName; + int ID; + QString HomeFolder; + QString Shell; + int GroupID; + friend bool operator<(const User lhs, const User rhs){ + return std::tie(lhs.ID,lhs.UserName) < std::tie(rhs.ID,rhs.UserName); + } + friend bool operator>(const User lhs, const User rhs) + { return rhs < lhs;} + friend bool operator==(const User lhs, const User rhs) + { + return lhs.ID == rhs.ID && lhs.UserName == rhs.UserName; + } + friend bool operator !=(const User lhs, const User rhs) + { return !(lhs == rhs);} +}; +class Group +{ +public: + int ID; + QString Name; + //While the object model would be more "correct" if + //Users were to be a Vector of User pointers, it's + //expensive to wire up and we don't really gain anything + //from doing so + QStringList Users; +}; + +class UserManager +{ +public: + UserManager(QString chroot = ""); + + //#section user actions + /** + * @brief NewUser Create a new user + * @param fullName The full name of the user + * @param userName The username of the user + * @param password The user's password + * @param home the user's home directory, defaults to /usr/home/$userName + * @param shell the user's shell, defaults to /bin/tcsh + * @param gid the gid of the user, defaults to -1, which means to leave it up to autogen + * @param uid the uid of the user, defaults to -1, which means to leave it up to autogen + */ + void NewUser(QString fullName, QString userName, QString password, QString home = "/usr/home/", QString shell = "/bin/tcsh", int gid = -1, int uid =-1); + void DeleteUser(User user); + + const QVector GetUsers(); + const User GetUser(int id); + const User GetUser(QString userName); + + void ChangeUserPassword(User user, QString newPassword); + void ChangeUserShell(User user, QString shell); + void ChangeUserFullName(User user, QString newName); + //#endsection + + //#section group actions + void AddUserToGroup(User user, Group group); + void RemoveUserFromGroup(User user, Group group); + + void NewGroup(QString name, QStringList Users = QStringList()); + void DeleteGroup(Group group); + + const QVector GetGroups(); + const Group getGroup(int id); + const Group getGroup(QString name); + //#endsection + + const QStringList GetShells(); + +private: + QVector users; + QVector groups; + QStringList shells; + QString chroot; + + void loadUsers(); + void loadGroups(); + void loadShells(); +}; +} +#endif // USERMANAGER_H From f570e39d04c19f984cf0405a6a00242e856d4118 Mon Sep 17 00:00:00 2001 From: dlavigne Date: Wed, 13 Jan 2016 14:51:20 -0500 Subject: [PATCH 02/20] Doc query. --- api/connection.rst | 100 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 14 deletions(-) diff --git a/api/connection.rst b/api/connection.rst index 22894c6..53bd018 100644 --- a/api/connection.rst +++ b/api/connection.rst @@ -1,21 +1,17 @@ -.. _Connection: - -Connection -========== - -Some intro text here... - .. _Getting Started: Getting Started ---------------- +*************** + +Some intro text here... + Add some links to docs on websockets and json.... .. _Authentication: Authentication --------------- +============== Describe how to authenticate to websockets via Local / Remote, local connections do not need username / password... @@ -44,7 +40,7 @@ request contains the following parameters: Several methods are available for authentication. Here is an example of a login using a username and password: -**Request** +**WebSocket Request** .. code-block:: json @@ -60,7 +56,7 @@ Several methods are available for authentication. Here is an example of a login Here is an example of using token authentication, where the token is invalidated after 5 minutes of inactivity: -**Request** +**WebSocket Request** .. code-block:: json @@ -75,7 +71,7 @@ Here is an example of using token authentication, where the token is invalidated A successful authentication will provide a reply similar to this: -**Reply** +**WebSocket Reply** .. code-block:: json @@ -95,7 +91,7 @@ A successful authentication will provide a reply similar to this: An invalid authentication, or a system request after the user session has timed out due to inactivity, looks like this: -**Reply** +**WebSocket Reply** .. code-block:: json @@ -111,7 +107,7 @@ An invalid authentication, or a system request after the user session has timed To clear a pre-saved authentication token, such as signing out, use this request: -**Request** +**WebSocket Request** .. code-block:: json @@ -121,3 +117,79 @@ To clear a pre-saved authentication token, such as signing out, use this request "id" : "sampleID", "args" : "junk argument" } + +.. _Server Subsystems: + +Server Subsystems +================= + +An RPC query can be issued to probe all the known subsystems and return which ones are currently available and what level of read and write access the user has. +A query contains the following parameters: + ++---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ +| **Parameter** | **Value** | **Description** | +| | | | ++=================================+===============+======================================================================================================================+ +| id | | any unique value for the request; examples include a hash, checksum, or uuid | +| | | | ++---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ +| name | query | | +| | | | ++---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ +| namespace | rpc | | +| | | | ++---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ +| args | | can be any data | +| | | | ++---------------------------------+---------------+----------------------------------------------------------------------------------------------------------------------+ + +**REST Request** + +.. code-block:: json + + PUT /rpc/query + { + "junk" : "junk" + } + +**REST Response** + +.. code-block:: json + + { + "args": { + "rpc/dispatcher": "read/write", + "rpc/syscache": "read", + "sysadm/lifepreserver": "read/write", + "sysadm/network": "read/write" + } + } + +**WebSocket Request** + +.. code-block:: json + + { + "id" : "fooid", + "name" : "query", + "namespace" : "rpc", + "args" : { + "junk" : "junk" + } + } + +**WebSocket Response** + +.. code-block:: json + + { + "args": { + "rpc/dispatcher": "read/write", + "rpc/syscache": "read", + "sysadm/lifepreserver": "read/write", + "sysadm/network": "read/write" + }, + "id": "fooid", + "name": "response", + "namespace": "rpc" + } \ No newline at end of file From 942d158db3c4a9adc149e9a7d543825640e89fe7 Mon Sep 17 00:00:00 2001 From: Luke De Mouy Date: Wed, 13 Jan 2016 16:05:31 -0700 Subject: [PATCH 03/20] Document the functionality to create a new user and transfer it over to using sysadm-general, this commit is done in preparation for simplifying things from the following: create zfs dataset -> create group associated with user -> manually create the home directory -> Create the user, passing in the flags that would have otherwise created the home directory normally, and sets the groupID to that of the group we created before (which it should automatically do if we don't specify a group) -> set the user password - > enable flash -> chown the home folder over to the user to: create zfs dataset -> create the user using appropriate flags -> set user password -> enable flash --- src/library/sysadm-usermanager.cpp | 188 +++++++++++++++++++---------- 1 file changed, 126 insertions(+), 62 deletions(-) diff --git a/src/library/sysadm-usermanager.cpp b/src/library/sysadm-usermanager.cpp index fa3ea6c..a248f4e 100644 --- a/src/library/sysadm-usermanager.cpp +++ b/src/library/sysadm-usermanager.cpp @@ -5,10 +5,12 @@ // See the LICENSE file for full details //=========================================== #include "sysadm-usermanager.h" +#include "sysadm-general.h" using namespace sysadm; UserManager::UserManager(QString chroot) { + this->chroot = chroot; loadUsers(); loadGroups(); loadShells(); @@ -16,6 +18,13 @@ UserManager::UserManager(QString chroot) void UserManager::NewUser(QString fullName, QString userName, QString password, QString home, QString shell, int gid, int uid) { + User user; + user.UserName = userName; + user.FullName = fullName; + user.HomeFolder = home; + user.Shell = shell; + user.ID = uid; + if (home == "/usr/home/") home += userName; //Add User @@ -23,70 +32,103 @@ void UserManager::NewUser(QString fullName, QString userName, QString password, // Create the new home-directory if ( chroot.isEmpty() ) { - system("/usr/local/share/pcbsd/scripts/mkzfsdir.sh " + home.toLatin1() ); - system("pw groupadd " + userName.toLatin1() ); + //create the home directory and a zfs dataset for it + QStringList args; + args.append(home); + General::RunCommand("/usr/local/share/pcbsd/scripts/mkzfsdir.sh",args); + + //create the group associated with the user + args.clear(); + args.append(userName); + General::RunCommand("pw groupadd",args); } else { - system("mkdir -p " + chroot.toLatin1() + "/" + home.toLatin1() + " 2>/dev/null" ); - system("chroot " + chroot.toLatin1() + " ln -s /usr/home /home 2>/dev/null" ); - system("chroot " + chroot.toLatin1() + " pw groupadd " + userName.toLatin1() ); + //make the home directory + QStringList args; + args.append("-p"); // create intermediate directories as needed + args.append(chroot); //chroot location + args.append(home); //directory to create, in this case home + args.append("2>/dev/null"); //send stderr to /dev/null + General::RunCommand("mkdir",args); + + //link /usr/home to /home + args.clear(); + args.append(chroot); //location the chroot is in + args.append("ln"); //link + args.append("-s"); //symbolic link + args.append("/usr/home"); //folder to link from + args.append("/home"); //The link to create + args.append("2>/dev/null"); //send stderr to /dev/null + General::RunCommand("chroot",args); + + //create the group associated with the user + args.clear(); + args.append(chroot); //location the chroot is in + args.append("pw groupadd"); //create a group + args.append(userName); //name of the group to create + General::RunCommand("chroot",args); } QStringList args; - if ( ! chroot.isEmpty() ) - args << chroot << "pw"; - args << "useradd"; - args << userName; - args << "-c"; - args << fullName; - args << "-m"; - args << "-d"; - args << home; - args << "-s"; - args << shell; - if (gid != -1) + if ( ! chroot.isEmpty() ) //if chroot is not empty the command starts with chroot instead of pw + args << chroot << "pw"; //and thus we have to add it as an argument + args << "useradd"; //create a user + args << userName; //with this userName + args << "-c"; //sets the comment field + args << fullName; //with the full name of the user + args << "-m"; //create the user's home directory + args << "-d"; //set the path of the user's home directory + args << home; //to here + args << "-s"; //set the user's shell + args << shell; //to this + if (gid != -1) //if a group id was specified { - args << "-g"; - args << QString::number(gid); - } else { - args << "-g"; - args << userName; + args << "-g"; //set the user's group to + args << QString::number(gid); // this + } else { //if a group id wasn't set + args << "-g"; //set the group + args << userName; //to a group with the same name as the user } - args << "-G"; - args << "operator"; - if( uid != -1) + args << "-G"; //additionally add the user to + args << "operator"; //the operator's group + if( uid != -1) //if the user set a UID { - args << "-u"; - args << QString::number( uid ); + args << "-u"; //set the UID + args << QString::number( uid ); //to this } - if ( ! chroot.isEmpty() ) - QProcess::execute("chroot", args); - else - QProcess::execute("pw", args); + if ( ! chroot.isEmpty() ) //if we're operating with a chroot call + General::RunCommand("chroot", args); + else //otherwise + General::RunCommand("pw", args); - QTemporaryFile nfile("/tmp/.XXXXXXXX"); - if ( nfile.open() ) - { - QTextStream stream( &nfile ); - stream << password; - nfile.close(); - } - if ( ! chroot.isEmpty() ) - system("cat " + nfile.fileName().toLatin1() + " | chroot " + chroot.toLatin1() + " pw usermod " + userName.toLatin1() + " -h 0 "); - else - system("cat " + nfile.fileName().toLatin1() + " | pw usermod " + userName.toLatin1() + " -h 0 "); - nfile.remove(); + ChangeUserPassword(user,password); - if ( chroot.isEmpty() ) { + //enable flash for the user + if ( chroot.isEmpty() ) { //if we're not in a chroot qDebug() << "Enabling Flash Plugin for " << userName; - QString flashCmd = "su " + userName + " -c \"flashpluginctl on\""; - system(flashCmd.toLatin1()); + args.clear(); + args.append(userName); //run command as this user + args.append("-c"); //with the command + args.append("\"flashpluginctl on\""); //turn on flashpluginctl + General::RunCommand("su",args); } // Set permissions - if ( chroot.isEmpty() ) - system("chown -R " + userName.toLatin1() +":" + userName.toLatin1() + " " + home.toLatin1() ); - else - system("chroot " + chroot.toLatin1() + " chown -R " + userName.toLatin1() +":" + userName.toLatin1() + " " + home.toLatin1() ); + if ( chroot.isEmpty() ) //if we're not in a chroot + { + //give the user ownership of their home directory + args.append("-R"); //Recursive, change the ownership of both folders and files + args.append(userName+":"+userName); //to the user + args.append(home); //their home directory + General::RunCommand("chown",args); + } else { //if we are in a chroot + //give the user ownership of their home directory + args.append(chroot); //the chroot location + args.append("chown"); //change ownership of + args.append("-R"); //Recursive, change the ownership of both folders and files + args.append(userName + ":" + userName); //to the user + args.append(home); //their home directory + General::RunCommand("chroot",args); + } //reloads the groups and users so that the internal model is consistent loadUsers(); @@ -148,21 +190,43 @@ const User UserManager::GetUser(QString userName) void UserManager::ChangeUserPassword(User user, QString newPassword) { - qDebug() << "Changing password: " << user.UserName; - // Set the new PW - QTemporaryFile rfile("/tmp/.XXXXXXXX"); - if ( rfile.open() ) { - QTextStream stream( &rfile ); - stream << newPassword; - rfile.close(); + //Create a temporary file to store the password in + QTemporaryFile nfile("/tmp/.XXXXXXXX"); + if ( nfile.open() ) + { + QTextStream stream( &nfile ); + stream << newPassword; + nfile.close(); } - if ( ! chroot.isEmpty() ) - system("cat " + rfile.fileName().toLatin1() + " | chroot " + chroot.toLatin1() + " pw usermod " + user.UserName.toLatin1() + " -h 0 "); - else - system("cat " + rfile.fileName().toLatin1() + " | pw usermod " + user.UserName.toLatin1() + " -h 0 "); - rfile.remove(); + if ( ! chroot.isEmpty() ) //if we're in a chroot + { + //set the user password + QStringList args; + args.append(nfile.fileName()); //the temp file holding the password + args.append("|"); //which we're going to pipe to the stdin of + args.append("chroot"); //a chroot + args.append(chroot); //located here + args.append("pw usermod"); //where we're going to modify a user + args.append(user.UserName);//this user + args.append("-h"); //set the user's password + args.append("0"); //using stdin + General::RunCommand("cat",args); + } + else + { + QStringList args; + args.append(nfile.fileName()); //the temp file holding the password + args.append("|"); //which we're going to pipe to the stdin of + args.append("pw usermod"); //and we're going to modify the user account of + args.append(user.UserName); //this user + args.append("-h"); //and we're going to set their password + args.append("0"); //using stdin + General::RunCommand("cat",args); + } + //remove the temp file holding the password + nfile.remove(); } From 3d16bc34563b4dc46aaae88ceb84939dcead973a Mon Sep 17 00:00:00 2001 From: Luke De Mouy Date: Wed, 13 Jan 2016 17:35:22 -0700 Subject: [PATCH 04/20] simplify the process for creating a new user and switch all system calls over to using General::RunCommand(); --- src/library/sysadm-usermanager.cpp | 164 +++++++++++------------------ src/library/sysadm-usermanager.h | 2 +- 2 files changed, 62 insertions(+), 104 deletions(-) diff --git a/src/library/sysadm-usermanager.cpp b/src/library/sysadm-usermanager.cpp index a248f4e..59ba987 100644 --- a/src/library/sysadm-usermanager.cpp +++ b/src/library/sysadm-usermanager.cpp @@ -16,56 +16,22 @@ UserManager::UserManager(QString chroot) loadShells(); } -void UserManager::NewUser(QString fullName, QString userName, QString password, QString home, QString shell, int gid, int uid) +void UserManager::NewUser(QString fullName, QString userName, QString password, QString shell) { User user; user.UserName = userName; user.FullName = fullName; - user.HomeFolder = home; + user.HomeFolder = "/usr/home/"+userName; user.Shell = shell; - user.ID = uid; - if (home == "/usr/home/") - home += userName; //Add User qDebug() << "Adding user " << userName; - // Create the new home-directory + // Create the zfs dataset associated with the home directory if ( chroot.isEmpty() ) { - //create the home directory and a zfs dataset for it QStringList args; - args.append(home); + args.append(user.HomeFolder); General::RunCommand("/usr/local/share/pcbsd/scripts/mkzfsdir.sh",args); - - //create the group associated with the user - args.clear(); - args.append(userName); - General::RunCommand("pw groupadd",args); - } else { - //make the home directory - QStringList args; - args.append("-p"); // create intermediate directories as needed - args.append(chroot); //chroot location - args.append(home); //directory to create, in this case home - args.append("2>/dev/null"); //send stderr to /dev/null - General::RunCommand("mkdir",args); - - //link /usr/home to /home - args.clear(); - args.append(chroot); //location the chroot is in - args.append("ln"); //link - args.append("-s"); //symbolic link - args.append("/usr/home"); //folder to link from - args.append("/home"); //The link to create - args.append("2>/dev/null"); //send stderr to /dev/null - General::RunCommand("chroot",args); - - //create the group associated with the user - args.clear(); - args.append(chroot); //location the chroot is in - args.append("pw groupadd"); //create a group - args.append(userName); //name of the group to create - General::RunCommand("chroot",args); } QStringList args; @@ -74,27 +40,13 @@ void UserManager::NewUser(QString fullName, QString userName, QString password, args << "useradd"; //create a user args << userName; //with this userName args << "-c"; //sets the comment field - args << fullName; //with the full name of the user + args << "\""+ fullName+"\""; //with the full name of the user args << "-m"; //create the user's home directory - args << "-d"; //set the path of the user's home directory - args << home; //to here args << "-s"; //set the user's shell args << shell; //to this - if (gid != -1) //if a group id was specified - { - args << "-g"; //set the user's group to - args << QString::number(gid); // this - } else { //if a group id wasn't set - args << "-g"; //set the group - args << userName; //to a group with the same name as the user - } args << "-G"; //additionally add the user to args << "operator"; //the operator's group - if( uid != -1) //if the user set a UID - { - args << "-u"; //set the UID - args << QString::number( uid ); //to this - } + if ( ! chroot.isEmpty() ) //if we're operating with a chroot call General::RunCommand("chroot", args); else //otherwise @@ -112,24 +64,6 @@ void UserManager::NewUser(QString fullName, QString userName, QString password, General::RunCommand("su",args); } - // Set permissions - if ( chroot.isEmpty() ) //if we're not in a chroot - { - //give the user ownership of their home directory - args.append("-R"); //Recursive, change the ownership of both folders and files - args.append(userName+":"+userName); //to the user - args.append(home); //their home directory - General::RunCommand("chown",args); - } else { //if we are in a chroot - //give the user ownership of their home directory - args.append(chroot); //the chroot location - args.append("chown"); //change ownership of - args.append("-R"); //Recursive, change the ownership of both folders and files - args.append(userName + ":" + userName); //to the user - args.append(home); //their home directory - General::RunCommand("chroot",args); - } - //reloads the groups and users so that the internal model is consistent loadUsers(); loadGroups(); @@ -140,23 +74,22 @@ void UserManager::DeleteUser(User user) //Delete User qDebug() << "Deleting user " << user.UserName; - /*if(userIt->getEnc()) - { - // Unmount PEFS - system("umount " + userIt->getHome().toLatin1() ); - }*/ + //remove the dataset associated with the home folder QStringList args; - if ( ! chroot.isEmpty() ) - args << chroot << "pw"; - args << "userdel"; - args << user.UserName; - args << "-r"; - system("/usr/local/share/pcbsd/scripts/rmzfsdir.sh " + user.HomeFolder.toLatin1() ); + args.append(user.HomeFolder); + General::RunCommand("/usr/local/share/pcbsd/scripts/rmzfsdir.sh",args); + //delete the user and their home directory + args.clear(); + if ( ! chroot.isEmpty() ) //if we're in a chroot we need to use chroot before pw + args << chroot << "pw"; + args << "userdel"; //delete a user + args << user.UserName; //this user + args << "-r"; //remove the contents of the user's home directory if ( ! chroot.isEmpty() ) - QProcess::execute("chroot", args); + General::RunCommand("chroot", args); else - QProcess::execute("pw", args); + General::RunCommand("pw", args); //update the internal model users.removeAll(user); @@ -233,55 +166,80 @@ void UserManager::ChangeUserPassword(User user, QString newPassword) void UserManager::ChangeUserShell(User user, QString shell) { if(shells.contains(shell)) - system("chsh -s " + shell.toLocal8Bit() + " " + user.UserName.toLocal8Bit()); + { + QStringList args; + args.append("-s"); //set the shell to + args.append(shell); //this shell + args.append(user.UserName); //for this user + General::RunCommand("chsh",args); + } else qDebug("Shell not found"); } void UserManager::ChangeUserFullName(User user, QString newName) { - system("pw usermod " + user.UserName.toLatin1() + " -c " + newName.toLatin1()); + QStringList args; + args.append(user.UserName); //for this user + args.append("-c"); //change the gecos field to + args.append(newName); //this name + General::RunCommand("pw usermod",args); } void UserManager::AddUserToGroup(User user, Group group) { - system("pw groupmod "+ group.Name.toLatin1() + " -m " + user.UserName.toLatin1()); + QStringList args; + args.append(group.Name);//modify this group + args.append("-m");//by adding a member + args.append(user.UserName); //this user + General::RunCommand("pw groupmod",args); + + group.Users.append(user.UserName); } void UserManager::RemoveUserFromGroup(User user, Group group) { - system("pw groupmod"+ group.Name.toLatin1() +"-d" + user.UserName.toLatin1()); + QStringList args; + args.append(group.Name); //modify this group + args.append("-d"); //by removing a user + args.append(user.UserName); //this user + General::RunCommand("pw groupmod", args); + + group.Users.removeAll(user.UserName); } void UserManager::NewGroup(QString name, QStringList members) { QStringList args; qDebug() << "Adding group " << name; - if ( ! chroot.isEmpty() ) + if ( ! chroot.isEmpty() ) //if we're in a chroot need to add chroot before pw args << chroot << "pw"; - args << "groupadd"; - args << name; - args << "-M"; - args << members.join(","); - if ( ! chroot.isEmpty() ) - QProcess::execute("chroot", args); + args << "groupadd"; //create a new group + args << name; // with this name + args << "-M"; //with this list of users + args << members.join(","); //these guys + if ( ! chroot.isEmpty() ) //if we're in a chroot + General::RunCommand("chroot", args); else - QProcess::execute("pw", args); + General::RunCommand("pw", args); + LoadGroups(); } void UserManager::DeleteGroup(Group group) { QStringList args; qDebug() << "Deleting group " << group.Name; - if ( ! chroot.isEmpty() ) + if ( ! chroot.isEmpty() ) //if we're in a chroot need to add chroot before pw args << chroot << "pw"; - args << "groupdel"; - args << group.Name; - if ( ! chroot.isEmpty() ) - QProcess::execute("chroot", args); + args << "groupdel"; //delete a group + args << group.Name; //of this name + if ( ! chroot.isEmpty() ) //if we're in a chroot + General::RunCommand("chroot", args); else - QProcess::execute("pw", args); + General::RunCommand("pw", args); + + LoadGroups(); } const QVector UserManager::GetGroups() diff --git a/src/library/sysadm-usermanager.h b/src/library/sysadm-usermanager.h index f7e1932..9ec0ba6 100644 --- a/src/library/sysadm-usermanager.h +++ b/src/library/sysadm-usermanager.h @@ -59,7 +59,7 @@ public: * @param gid the gid of the user, defaults to -1, which means to leave it up to autogen * @param uid the uid of the user, defaults to -1, which means to leave it up to autogen */ - void NewUser(QString fullName, QString userName, QString password, QString home = "/usr/home/", QString shell = "/bin/tcsh", int gid = -1, int uid =-1); + void NewUser(QString fullName, QString userName, QString password, QString shell = "/bin/tcsh"); void DeleteUser(User user); const QVector GetUsers(); From b2368aadd4dd917905fd25244c836c69a93bf9c6 Mon Sep 17 00:00:00 2001 From: Luke De Mouy Date: Wed, 13 Jan 2016 17:52:09 -0700 Subject: [PATCH 05/20] Document all of the member functions in the header --- src/library/sysadm-usermanager.cpp | 1 + src/library/sysadm-usermanager.h | 84 ++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/library/sysadm-usermanager.cpp b/src/library/sysadm-usermanager.cpp index 59ba987..af2f2f3 100644 --- a/src/library/sysadm-usermanager.cpp +++ b/src/library/sysadm-usermanager.cpp @@ -339,6 +339,7 @@ void UserManager::loadGroups() void UserManager::loadShells() { + shells.clear(); QFile shellFile(chroot + "/etc/shells"); if ( shellFile.open(QIODevice::ReadOnly) ) { QTextStream stream(&shellFile); diff --git a/src/library/sysadm-usermanager.h b/src/library/sysadm-usermanager.h index 9ec0ba6..3467395 100644 --- a/src/library/sysadm-usermanager.h +++ b/src/library/sysadm-usermanager.h @@ -54,35 +54,110 @@ public: * @param fullName The full name of the user * @param userName The username of the user * @param password The user's password - * @param home the user's home directory, defaults to /usr/home/$userName * @param shell the user's shell, defaults to /bin/tcsh - * @param gid the gid of the user, defaults to -1, which means to leave it up to autogen - * @param uid the uid of the user, defaults to -1, which means to leave it up to autogen */ void NewUser(QString fullName, QString userName, QString password, QString shell = "/bin/tcsh"); + /** + * @brief DeleteUser Deletes a user + * @param user the user to delete + */ void DeleteUser(User user); + /** + * @brief GetUsers getter for the users vector + * @return a QVector that is a copy of the current state + * do not modify it, instead call functions on this class to change + * things and then get another copy of the vector + */ const QVector GetUsers(); + /** + * @brief GetUser get a particular user by their UID + * @param id the UID of the user to get + * @return the user with the UID specified, if not found + * returns a blank User + */ const User GetUser(int id); + + /** + * @brief GetUser get a particular user by their UID + * @param userName the username of the user to get + * @return the user with the user name specified, if not found + * returns a blank User + */ const User GetUser(QString userName); + /** + * @brief ChangeUserPassword changes the specified user's password + * @param user the user to change the password of + * @param newPassword the new password + */ void ChangeUserPassword(User user, QString newPassword); + /** + * @brief ChangeUserShell change a specified user's shell + * @param user the user to change the shell for + * @param shell the shell to change to, note that if the shell + * is not in the shells list then it does nothing + */ void ChangeUserShell(User user, QString shell); + /** + * @brief ChangeUserFullName change the gecos field of a user to a new name + * @param user the user to change the name of + * @param newName the name to change to + */ void ChangeUserFullName(User user, QString newName); //#endsection //#section group actions + /** + * @brief AddUserToGroup add the specified user to the specified group + * @param user the user to add to the group + * @param group the group to add the user to + */ void AddUserToGroup(User user, Group group); + /** + * @brief RemoveUserFromGroup removes the specified user from the specified group + * @param user the user to remove from the group + * @param group the group to remove the user from + */ void RemoveUserFromGroup(User user, Group group); + /** + * @brief NewGroup creates a new group + * @param name the name of the new group + * @param Users a list of users to add to the group + */ void NewGroup(QString name, QStringList Users = QStringList()); + /** + * @brief DeleteGroup delete a specified group + * @param group the group to delete + */ void DeleteGroup(Group group); + /** + * @brief GetGroups get the internal list of groups + * @return a QVector that is a copy of the current state + * do not modify it, instead call functions on this class to change + * things and then get another copy of the vector + */ const QVector GetGroups(); + /** + * @brief getGroup get a specified group by their gid + * @param id the gid of the group to get + * @return the group with the specified gid + */ const Group getGroup(int id); + /** + * @brief getGroup get a specified group by their name + * @param name the name of the group to get + * @return the group with the specified name + */ const Group getGroup(QString name); //#endsection + /** + * @brief GetShells the list of shells that are currently installed on the system + * @return a QStringList of shells on the system + */ const QStringList GetShells(); private: @@ -91,8 +166,11 @@ private: QStringList shells; QString chroot; + //loads the users from /etc/passwd void loadUsers(); + //load the groups from /etc/group void loadGroups(); + //load the shells from /etc/shells void loadShells(); }; } From dbe7a84c3eadd8188f8c253ba9664bb26e52c471 Mon Sep 17 00:00:00 2001 From: Luke De Mouy Date: Wed, 13 Jan 2016 18:04:14 -0700 Subject: [PATCH 06/20] Simply password changing code so that it doesn't repeat itself between being and not being in a chroot --- src/library/sysadm-usermanager.cpp | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/library/sysadm-usermanager.cpp b/src/library/sysadm-usermanager.cpp index af2f2f3..6bf7f3b 100644 --- a/src/library/sysadm-usermanager.cpp +++ b/src/library/sysadm-usermanager.cpp @@ -133,31 +133,21 @@ void UserManager::ChangeUserPassword(User user, QString newPassword) nfile.close(); } + //set the user password + QStringList args; + args.append(nfile.fileName()); //the temp file holding the password + args.append("|"); //which we're going to pipe to the stdin of if ( ! chroot.isEmpty() ) //if we're in a chroot { - //set the user password - QStringList args; - args.append(nfile.fileName()); //the temp file holding the password - args.append("|"); //which we're going to pipe to the stdin of args.append("chroot"); //a chroot args.append(chroot); //located here - args.append("pw usermod"); //where we're going to modify a user - args.append(user.UserName);//this user - args.append("-h"); //set the user's password - args.append("0"); //using stdin - General::RunCommand("cat",args); - } - else - { - QStringList args; - args.append(nfile.fileName()); //the temp file holding the password - args.append("|"); //which we're going to pipe to the stdin of - args.append("pw usermod"); //and we're going to modify the user account of - args.append(user.UserName); //this user - args.append("-h"); //and we're going to set their password - args.append("0"); //using stdin - General::RunCommand("cat",args); } + args.append("pw usermod"); //where we're going to modify a user + args.append(user.UserName);//this user + args.append("-h"); //set the user's password + args.append("0"); //using stdin + General::RunCommand("cat",args); + //remove the temp file holding the password nfile.remove(); From b099e477eef46d63bd426aef495cecc8b72dbc16 Mon Sep 17 00:00:00 2001 From: Luke De Mouy Date: Wed, 13 Jan 2016 19:49:05 -0700 Subject: [PATCH 07/20] various fixes to make things work and a few style changes, namely General::ExecuteCommand cannot execute a command with a space in it so it has to be added as an arg, and switched from .append to << --- src/library/sysadm-usermanager.cpp | 81 +++++++++++++++++------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/src/library/sysadm-usermanager.cpp b/src/library/sysadm-usermanager.cpp index 6bf7f3b..18ead38 100644 --- a/src/library/sysadm-usermanager.cpp +++ b/src/library/sysadm-usermanager.cpp @@ -58,9 +58,9 @@ void UserManager::NewUser(QString fullName, QString userName, QString password, if ( chroot.isEmpty() ) { //if we're not in a chroot qDebug() << "Enabling Flash Plugin for " << userName; args.clear(); - args.append(userName); //run command as this user - args.append("-c"); //with the command - args.append("\"flashpluginctl on\""); //turn on flashpluginctl + args << userName; //run command as this user + args << "-c"; //with the command + args << "\"flashpluginctl on\""; //turn on flashpluginctl General::RunCommand("su",args); } @@ -76,7 +76,7 @@ void UserManager::DeleteUser(User user) //remove the dataset associated with the home folder QStringList args; - args.append(user.HomeFolder); + args << user.HomeFolder; General::RunCommand("/usr/local/share/pcbsd/scripts/rmzfsdir.sh",args); //delete the user and their home directory @@ -89,10 +89,9 @@ void UserManager::DeleteUser(User user) if ( ! chroot.isEmpty() ) General::RunCommand("chroot", args); else - General::RunCommand("pw", args); + General::RunCommand("pw", args); - //update the internal model - users.removeAll(user); + loadUsers(); loadGroups(); } @@ -139,13 +138,14 @@ void UserManager::ChangeUserPassword(User user, QString newPassword) args.append("|"); //which we're going to pipe to the stdin of if ( ! chroot.isEmpty() ) //if we're in a chroot { - args.append("chroot"); //a chroot - args.append(chroot); //located here + args << "chroot"; //a chroot + args << chroot; //located here } - args.append("pw usermod"); //where we're going to modify a user - args.append(user.UserName);//this user - args.append("-h"); //set the user's password - args.append("0"); //using stdin + args << "pw"; //change users + args << "usermod"; //where we're going to modify a user + args << user.UserName;//this user + args << "-h"; //set the user's password + args << "0"; //using stdin General::RunCommand("cat",args); //remove the temp file holding the password @@ -157,45 +157,56 @@ void UserManager::ChangeUserShell(User user, QString shell) { if(shells.contains(shell)) { + qDebug("Shell found"); QStringList args; - args.append("-s"); //set the shell to - args.append(shell); //this shell - args.append(user.UserName); //for this user - General::RunCommand("chsh",args); + args << "usermod"; // modify the user + args << "-n"; //specify a user name + args << user.UserName; //for this user + args << "-s"; //set the shell to + args << shell; //this shell + General::RunCommand("pw",args); } else qDebug("Shell not found"); + + loadUsers(); } void UserManager::ChangeUserFullName(User user, QString newName) { QStringList args; - args.append(user.UserName); //for this user - args.append("-c"); //change the gecos field to - args.append(newName); //this name - General::RunCommand("pw usermod",args); + args << "usermod"; //modify the user + args << user.UserName; //for this user + args << "-c"; //change the gecos field to + args << newName; //this name + General::RunCommand("pw",args); + loadUsers(); } void UserManager::AddUserToGroup(User user, Group group) { QStringList args; - args.append(group.Name);//modify this group - args.append("-m");//by adding a member - args.append(user.UserName); //this user - General::RunCommand("pw groupmod",args); + args << "groupmod"; //modify a group + args << "-n"; //modify for a group + args << group.Name;//this group + args << "-m";//by adding a member + args << user.UserName; //this user + General::RunCommand("pw",args); - group.Users.append(user.UserName); + loadGroups(); } void UserManager::RemoveUserFromGroup(User user, Group group) { QStringList args; - args.append(group.Name); //modify this group - args.append("-d"); //by removing a user - args.append(user.UserName); //this user - General::RunCommand("pw groupmod", args); + args << "groupmod"; //modify a group + args << "-n"; //modify for a group + args << group.Name; //this group + args << "-d"; //by removing a user + args << user.UserName ; //this user + General::RunCommand("pw", args); - group.Users.removeAll(user.UserName); + loadGroups(); } void UserManager::NewGroup(QString name, QStringList members) @@ -213,7 +224,7 @@ void UserManager::NewGroup(QString name, QStringList members) else General::RunCommand("pw", args); - LoadGroups(); + loadGroups(); } void UserManager::DeleteGroup(Group group) @@ -229,7 +240,7 @@ void UserManager::DeleteGroup(Group group) else General::RunCommand("pw", args); - LoadGroups(); + loadGroups(); } const QVector UserManager::GetGroups() @@ -345,8 +356,8 @@ void UserManager::loadShells() } } } else { - //Unable to open file error - qWarning("Error! Unable to open /etc/shells"); + //Unable to open file error + qWarning("Error! Unable to open /etc/shells"); } // Add /sbin/nologin as well From 66c878697cd92f6520b21f2a471b3a673ce9b7d1 Mon Sep 17 00:00:00 2001 From: Luke De Mouy Date: Wed, 13 Jan 2016 20:38:42 -0700 Subject: [PATCH 08/20] Add Default Constructors to users and groups, and change Users to Members in the Group class. --- src/library/sysadm-usermanager.cpp | 2 +- src/library/sysadm-usermanager.h | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/library/sysadm-usermanager.cpp b/src/library/sysadm-usermanager.cpp index 18ead38..9586bf6 100644 --- a/src/library/sysadm-usermanager.cpp +++ b/src/library/sysadm-usermanager.cpp @@ -327,7 +327,7 @@ void UserManager::loadGroups() group.Name = line.section(":",0,0); group.ID = line.section(":",2,2).toInt(); QString memberString = line.section(":",3,3); - group.Users = memberString.split(","); + group.Members = memberString.split(","); groups.append(group); } diff --git a/src/library/sysadm-usermanager.h b/src/library/sysadm-usermanager.h index 3467395..cc908c8 100644 --- a/src/library/sysadm-usermanager.h +++ b/src/library/sysadm-usermanager.h @@ -13,6 +13,16 @@ namespace sysadm{ class User { public: + User() + { + FullName = ""; + UserName = ""; + ID = -1; + HomeFolder = ""; + Shell = ""; + GroupID = -1; + } + QString FullName; QString UserName; int ID; @@ -34,13 +44,20 @@ public: class Group { public: + Group() + { + ID = -1; + Name = ""; + Members = QStringList(); + } + int ID; QString Name; //While the object model would be more "correct" if //Users were to be a Vector of User pointers, it's //expensive to wire up and we don't really gain anything //from doing so - QStringList Users; + QStringList Members; }; class UserManager From 553b14185bf0b80cd15f24206a9eac84c088d73c Mon Sep 17 00:00:00 2001 From: Luke De Mouy Date: Thu, 14 Jan 2016 11:15:14 -0700 Subject: [PATCH 09/20] add ability to set the uid and gid when creating a user back in --- src/library/sysadm-usermanager.cpp | 14 ++++++++++++-- src/library/sysadm-usermanager.h | 8 ++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/library/sysadm-usermanager.cpp b/src/library/sysadm-usermanager.cpp index 9586bf6..929d5e3 100644 --- a/src/library/sysadm-usermanager.cpp +++ b/src/library/sysadm-usermanager.cpp @@ -16,7 +16,7 @@ UserManager::UserManager(QString chroot) loadShells(); } -void UserManager::NewUser(QString fullName, QString userName, QString password, QString shell) +void UserManager::NewUser(QString fullName, QString userName, QString password, QString shell, int uid, int gid) { User user; user.UserName = userName; @@ -44,6 +44,16 @@ void UserManager::NewUser(QString fullName, QString userName, QString password, args << "-m"; //create the user's home directory args << "-s"; //set the user's shell args << shell; //to this + if(gid != -1) + { + args << "-g"; + args << gid; + } + if(uid != -1) + { + args << "-u"; + args << uid; + } args << "-G"; //additionally add the user to args << "operator"; //the operator's group @@ -89,7 +99,7 @@ void UserManager::DeleteUser(User user) if ( ! chroot.isEmpty() ) General::RunCommand("chroot", args); else - General::RunCommand("pw", args); + General::RunCommand("pw", args); loadUsers(); loadGroups(); diff --git a/src/library/sysadm-usermanager.h b/src/library/sysadm-usermanager.h index cc908c8..77ae8b8 100644 --- a/src/library/sysadm-usermanager.h +++ b/src/library/sysadm-usermanager.h @@ -21,6 +21,7 @@ public: HomeFolder = ""; Shell = ""; GroupID = -1; + Encrypted = false; } QString FullName; @@ -29,6 +30,7 @@ public: QString HomeFolder; QString Shell; int GroupID; + bool Encrypted; friend bool operator<(const User lhs, const User rhs){ return std::tie(lhs.ID,lhs.UserName) < std::tie(rhs.ID,rhs.UserName); } @@ -62,7 +64,7 @@ public: class UserManager { -public: +public: UserManager(QString chroot = ""); //#section user actions @@ -72,8 +74,10 @@ public: * @param userName The username of the user * @param password The user's password * @param shell the user's shell, defaults to /bin/tcsh + * @param uid the user id of the user + * @param gid the group id of the user */ - void NewUser(QString fullName, QString userName, QString password, QString shell = "/bin/tcsh"); + void NewUser(QString fullName, QString userName, QString password, QString shell = "/bin/tcsh", int uid = -1, int gid = -1); /** * @brief DeleteUser Deletes a user * @param user the user to delete From 48d7108529e89dd57131c963aabd1a4d36b3bb76 Mon Sep 17 00:00:00 2001 From: Kris Moore Date: Thu, 14 Jan 2016 15:17:25 -0500 Subject: [PATCH 10/20] Add a new API call to (re)init the replication on the remote side via life-preserver dataset = The local dataset/pool you are replicating host = The target for replication, either IP or hostname REST Request: ------------------------------- PUT /sysadm/lifepreserver { "dataset" : "tank1", "host" : "192.168.0.9", "action" : "initreplication" } REST Response: ------------------------------- { "args": { "initreplication": { "dataset": "tank1", "host": "192.168.0.9" } } } WebSocket Request: ------------------------------- { "id" : "fooid", "args" : { "host" : "192.168.0.9", "dataset" : "tank1", "action" : "initreplication" }, "namespace" : "sysadm", "name" : "lifepreserver" } WebSocket Response: ------------------------------- { "args": { "initreplication": { "dataset": "tank1", "host": "192.168.0.9" } }, "id": "fooid", "name": "response", "namespace": "sysadm" } --- src/library/sysadm-lifepreserver.cpp | 40 ++++++++++++++++++++++++++++ src/library/sysadm-lifepreserver.h | 1 + src/server/WebBackend.cpp | 4 +++ 3 files changed, 45 insertions(+) diff --git a/src/library/sysadm-lifepreserver.cpp b/src/library/sysadm-lifepreserver.cpp index ab4c73a..a9bc8f5 100644 --- a/src/library/sysadm-lifepreserver.cpp +++ b/src/library/sysadm-lifepreserver.cpp @@ -83,6 +83,46 @@ QJsonObject LifePreserver::addReplication(QJsonObject jsin) { return values; } +// Re-init the LP replication target +QJsonObject LifePreserver::initReplication(QJsonObject jsin) { + QJsonObject retObject; + QString dset, rhost; + + QStringList keys = jsin.keys(); + if(! keys.contains("dataset") || ! keys.contains("host")){ + retObject.insert("error", "Missing dataset or host key"); + return retObject; + } + + // Check which pool we are looking at + dset = jsin.value("dataset").toString(); + rhost = jsin.value("host").toString(); + + // Make sure we have the pool key + if ( dset.isEmpty() || rhost.isEmpty()) { + retObject.insert("error", "Missing dataset or host key"); + return retObject; + } + + // TODO - This command can take a LONG TIME. Find a way to queue / background it and return an event + // via websockets later, or block here and return when finished if this is REST + QStringList output = General::RunCommand("lpreserver replicate init " + dset + " " + rhost).split("\n"); + + // Check for any errors + for ( int i = 0; i < output.size(); i++) + { + if ( output.at(i).indexOf("ERROR:") != -1 ) { + retObject.insert("error", output.at(i)); + return retObject; + } + } + + QJsonObject values; + values.insert("dataset", dset); + values.insert("host", rhost); + return values; +} + // Build list of scheduled cron snapshot jobs QJsonObject LifePreserver::listCron() { QJsonObject retObject; diff --git a/src/library/sysadm-lifepreserver.h b/src/library/sysadm-lifepreserver.h index 3954d4f..66708b8 100644 --- a/src/library/sysadm-lifepreserver.h +++ b/src/library/sysadm-lifepreserver.h @@ -15,6 +15,7 @@ namespace sysadm{ class LifePreserver{ public: static QJsonObject addReplication(QJsonObject jsin); + static QJsonObject initReplication(QJsonObject jsin); static QJsonObject listCron(); static QJsonObject listSnap(QJsonObject jsin); static QJsonObject removeSnapshot(QJsonObject jsin); diff --git a/src/server/WebBackend.cpp b/src/server/WebBackend.cpp index f4ef0a2..9ffc94a 100644 --- a/src/server/WebBackend.cpp +++ b/src/server/WebBackend.cpp @@ -189,6 +189,10 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmLifePreserverRequest(const Q ok = true; out->insert("cronsnap", sysadm::LifePreserver::scheduleSnapshot(in_args.toObject())); } + if(act=="initreplication"){ + ok = true; + out->insert("initreplication", sysadm::LifePreserver::initReplication(in_args.toObject())); + } if(act=="listcron"){ ok = true; out->insert("listcron", sysadm::LifePreserver::listCron()); From 3347669b6e1edfffd8361dfd4fdddd2b488114e1 Mon Sep 17 00:00:00 2001 From: Luke De Mouy Date: Thu, 14 Jan 2016 15:01:04 -0700 Subject: [PATCH 11/20] Copy the PersonaCrypt Functions from the maindlgcode file and wire it up to user creation and password changing --- src/library/sysadm-usermanager.cpp | 182 ++++++++++++++++++++++++++++- src/library/sysadm-usermanager.h | 35 +++++- 2 files changed, 210 insertions(+), 7 deletions(-) diff --git a/src/library/sysadm-usermanager.cpp b/src/library/sysadm-usermanager.cpp index 929d5e3..fa8a0dc 100644 --- a/src/library/sysadm-usermanager.cpp +++ b/src/library/sysadm-usermanager.cpp @@ -16,12 +16,12 @@ UserManager::UserManager(QString chroot) loadShells(); } -void UserManager::NewUser(QString fullName, QString userName, QString password, QString shell, int uid, int gid) +void UserManager::NewUser(QString fullName, QString userName, QString password, QString home, QString shell, int uid, int gid, bool encrypt) { User user; user.UserName = userName; user.FullName = fullName; - user.HomeFolder = "/usr/home/"+userName; + user.HomeFolder = (home.isEmpty())?"/usr/home/"+userName : home; user.Shell = shell; //Add User @@ -42,17 +42,22 @@ void UserManager::NewUser(QString fullName, QString userName, QString password, args << "-c"; //sets the comment field args << "\""+ fullName+"\""; //with the full name of the user args << "-m"; //create the user's home directory + if(!home.isEmpty()) + { + args << "-d"; //set the home directory to + args << home; //this + } args << "-s"; //set the user's shell args << shell; //to this if(gid != -1) { - args << "-g"; - args << gid; + args << "-g"; //set the group id to + args << QString::number(gid); //this } if(uid != -1) { - args << "-u"; - args << uid; + args << "-u"; //set the user id to + args << QString::number(uid); //this } args << "-G"; //additionally add the user to args << "operator"; //the operator's group @@ -74,6 +79,10 @@ void UserManager::NewUser(QString fullName, QString userName, QString password, General::RunCommand("su",args); } + //if we're going to PersonaCrypt the home directory + if(encrypt) + initPCDevice(user,home,password); + //reloads the groups and users so that the internal model is consistent loadUsers(); loadGroups(); @@ -132,6 +141,8 @@ const User UserManager::GetUser(QString userName) void UserManager::ChangeUserPassword(User user, QString newPassword) { + //Don't Change the password of a user with an encrypted Home directory + if( !QFile::exists("/var/db/personacrypt/"+user.UserName+".key") ){ return; } //Create a temporary file to store the password in QTemporaryFile nfile("/tmp/.XXXXXXXX"); @@ -373,3 +384,162 @@ void UserManager::loadShells() // Add /sbin/nologin as well shells.append("/sbin/nologin"); } + + +void UserManager::importPCKey(User user, QString filename){ + //Double check that the key does not exist (button should have been hidden earlier if invalid) + if( QFile::exists("/var/db/personacrypt/"+user.UserName+".key") ){ return; } + + //if the location is empty cancel + if(filename.isEmpty()){ return; } + + //Now run the import command + QStringList args; + args << "import"; + args << "\""+filename + "\""; + if( 0 == General::RunCommand("personacrypt",args) ){ + //Success + qDebug("The key file was imported successfully."); + }else{ + //Failure + qWarning("The key file could not be imported. Please ensure you are using a valid file."); + } +} + +void UserManager::exportPCKey(User user, QString filename){ + //Double check that the key exists (button should have been hidden earlier if invalid) + if( !QFile::exists("/var/db/personacrypt/"+user.UserName+".key") ){ return; } + + if(filename.isEmpty()){ return; } //cancelled + if( !filename.endsWith(".key") ){ filename.append(".key"); } + //Now get/save the key file + QStringList args; + args << "export"; + args << "\"" + user.UserName + "\""; + QString key = General::RunCommand("personacrypt",args); + + QFile file(filename); + if( !file.open(QIODevice::WriteOnly | QIODevice::Truncate) ){ + //Could not open output file + qWarning() <<"Output file could not be opened:\n\n" << filename; + return; + } + QTextStream out(&file); + out << key; + file.close(); + qDebug() << "The PersonaCrypt key has been saved successfully: \n\n" << filename; +} + +void UserManager::disablePCKey(User user){ +//Double check that the key exists (button should have been hidden earlier if invalid) + if( !QFile::exists("/var/db/personacrypt/"+user.UserName+".key") ){ return; } + + if( QFile::remove("/var/db/personacrypt/"+user.UserName+".key") ){ + //Success + qDebug("The PersonaCrypt user key has been disabled." ); + }else{ + //Failure (should almost never happen, since this utility runs as root and just needs to delete a file) + qDebug("The PersonaCrypt user key could not be removed. Do you have the proper permissions?" ); + } +} + +void UserManager::disableAndCopyPCKey(User user, QString password){ + QStringList args; + args << "list"; + QStringList cusers = General::RunCommand("personacrypt",args).split("\n"); + bool available = false; + for(int i=0; i