Merge pull request #4 from ldemouy/master

Create a UserManager for sysadm
This commit is contained in:
Ken Moore
2016-01-14 10:14:09 -05:00
3 changed files with 564 additions and 3 deletions

View File

@@ -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

View File

@@ -0,0 +1,365 @@
//===========================================
// 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"
#include "sysadm-general.h"
using namespace sysadm;
UserManager::UserManager(QString chroot)
{
this->chroot = chroot;
loadUsers();
loadGroups();
loadShells();
}
void UserManager::NewUser(QString fullName, QString userName, QString password, QString shell)
{
User user;
user.UserName = userName;
user.FullName = fullName;
user.HomeFolder = "/usr/home/"+userName;
user.Shell = shell;
//Add User
qDebug() << "Adding user " << userName;
// Create the zfs dataset associated with the home directory
if ( chroot.isEmpty() )
{
QStringList args;
args.append(user.HomeFolder);
General::RunCommand("/usr/local/share/pcbsd/scripts/mkzfsdir.sh",args);
}
QStringList args;
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 << "-s"; //set the user's shell
args << shell; //to this
args << "-G"; //additionally add the user to
args << "operator"; //the operator's group
if ( ! chroot.isEmpty() ) //if we're operating with a chroot call
General::RunCommand("chroot", args);
else //otherwise
General::RunCommand("pw", args);
ChangeUserPassword(user,password);
//enable flash for the user
if ( chroot.isEmpty() ) { //if we're not in a chroot
qDebug() << "Enabling Flash Plugin for " << userName;
args.clear();
args << userName; //run command as this user
args << "-c"; //with the command
args << "\"flashpluginctl on\""; //turn on flashpluginctl
General::RunCommand("su",args);
}
//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;
//remove the dataset associated with the home folder
QStringList args;
args << 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() )
General::RunCommand("chroot", args);
else
General::RunCommand("pw", args);
loadUsers();
loadGroups();
}
const QVector<User> 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)
{
//Create a temporary file to store the password in
QTemporaryFile nfile("/tmp/.XXXXXXXX");
if ( nfile.open() )
{
QTextStream stream( &nfile );
stream << 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
{
args << "chroot"; //a chroot
args << chroot; //located here
}
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
nfile.remove();
}
void UserManager::ChangeUserShell(User user, QString shell)
{
if(shells.contains(shell))
{
qDebug("Shell found");
QStringList 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 << "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 << "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);
loadGroups();
}
void UserManager::RemoveUserFromGroup(User user, Group group)
{
QStringList 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);
loadGroups();
}
void UserManager::NewGroup(QString name, QStringList members)
{
QStringList args;
qDebug() << "Adding group " << name;
if ( ! chroot.isEmpty() ) //if we're in a chroot need to add chroot before pw
args << chroot << "pw";
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
General::RunCommand("pw", args);
loadGroups();
}
void UserManager::DeleteGroup(Group group)
{
QStringList args;
qDebug() << "Deleting group " << group.Name;
if ( ! chroot.isEmpty() ) //if we're in a chroot need to add chroot before pw
args << chroot << "pw";
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
General::RunCommand("pw", args);
loadGroups();
}
const QVector<Group> 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.Members = memberString.split(",");
groups.append(group);
}
}
} else {
//Unable to open file error
qWarning("Error! Unable to open /etc/group");
}
}
void UserManager::loadShells()
{
shells.clear();
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");
}

View File

@@ -0,0 +1,194 @@
//===========================================
// 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<QtCore>
namespace sysadm{
class User
{
public:
User()
{
FullName = "";
UserName = "";
ID = -1;
HomeFolder = "";
Shell = "";
GroupID = -1;
}
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:
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 Members;
};
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 shell the user's shell, defaults to /bin/tcsh
*/
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<Users> 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<User> 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<Group> 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<Group> 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:
QVector<User> users;
QVector<Group> groups;
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();
};
}
#endif // USERMANAGER_H