From: konrad Date: Sun, 13 Jan 2008 18:01:08 +0000 (+0000) Subject: start of user management X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=b13e8308a529b705220516fc38fc8cf8f94fbd1f;p=web%2Fkonrad%2Fsmoke.git start of user management git-svn-id: https://silmor.de/svn/softmagic/smoke/trunk@83 6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33 --- diff --git a/doc/prog_protocol.html b/doc/prog_protocol.html index 486d1c3..2f5205c 100644 --- a/doc/prog_protocol.html +++ b/doc/prog_protocol.html @@ -4,7 +4,7 @@

Magic Smoke Protocol Documentation

logo
-The Magic Smoke Protocol is used between the Magic Smoke Server and Client. It is based on HTTP and XML. Only POST requests are allowed.

+The Magic Smoke Protocol is used between the Magic Smoke Server and Client. It is based on HTTP and XML. Only POST requests are allowed. All body data is presumed to be in UTF-8 encoding, headers are restricted to ASCII-127.

Each request must have a "x-magicsmoke-request=..." header. The actual request data is sent as POST-data. Except for the serverinfo and startsession requests all requests must also carry a "x-magicsmoke-session" header. @@ -110,6 +110,12 @@ This is done with a sessionclose request. Neither request nor response This request always yields an "Ok" status response regardless of whether the session ID was still valid or not. +

Note on Usernames and Roles

+ +Both, user names and roles, are restricted to letters, digits and "_". Leading and trailing whitespace is ignored (trimmed).

+ +Each role corresponds to a function that can be called and executed remotely through the machine interface. Roles beginning with "_" are reserved keywords that can alter the behaviour of other roles and must not be used to name functions. +

Basic Requests

Getting ACL info

@@ -231,3 +237,46 @@ The setroomdata transaction is used to create/change one or more rooms, The response simply contains the status "Ok" or "Error". + +

ACL related Transactions

+ +

Getting User Info

+ +The getusers transaction returns the names and descriptions of all users in the system. The request is empty, the response looks like:

+ +

+<Users>
+  <User name="loginname">Description of User</User>
+  ...
+</Users>
+
+ +

Adding Users and Setting New Descriptions

+ +The setuserdescription call can be used to set new descriptions for existing users. The call may silently fail for any user that does not exist, hence the client should request user data from the server to confirm whether the call succeeded.

+ +The adduser call can be used to create new user accounts. Initially the new user will have no ACL and no host settings.

+ +Both calls use this structure for the request:

+ +

+<Users>
+  <User name="loginname">Description of User</User>
+  ...
+</Users>
+
+ +The adduser call also uses it for the response, leaving out any user name that already existed or does not conform to the syntax requirements. + +

Getting User ACLs

+ +The getuseracl transaction returns the ACL of a specific user. The request contains only the login name of the user. The response looks like:

+ +

+<ACL user="username">
+  <Role name="rolename" set="1|0"/>
+  ...
+</ACL>
+
+ +All setable roles must be listed, so that the client can use these for displaying in a dialog. diff --git a/src/overview.cpp b/src/overview.cpp index 2e06dd9..34c71c4 100644 --- a/src/overview.cpp +++ b/src/overview.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -117,6 +118,36 @@ MOverview::MOverview(MWebRequest*mw,QString pk) hl->addWidget(new QPushButton(tr("Save Order"))); hl->addWidget(new QPushButton(tr("Clear"))); + //user tab + tab->addTab(usertab=new QWidget,tr("Users")); + usertab->setLayout(hl=new QHBoxLayout); + hl->addWidget(usertable=new QTableView,10); + usertable->setModel(usermodel=new QStandardItemModel(this)); + usertable->setSelectionMode(QAbstractItemView::SingleSelection); + usertable->setEditTriggers(QAbstractItemView::NoEditTriggers); + hl->addSpacing(5); + hl->addLayout(vl=new QVBoxLayout,0); + vl->addWidget(p=new QPushButton(tr("New User...")),0); + connect(p,SIGNAL(clicked()),this,SLOT(newUser())); + p->setEnabled(req->hasRole("adduser")); + vl->addWidget(p=new QPushButton(tr("Delete User...")),0); + connect(p,SIGNAL(clicked()),this,SLOT(deleteUser())); + p->setEnabled(req->hasRole("deleteuser")); + vl->addSpacing(20); + vl->addWidget(p=new QPushButton(tr("Description...")),0); + connect(p,SIGNAL(clicked()),this,SLOT(editUserDescription())); + p->setEnabled(req->hasRole("setuserdescription")); + vl->addWidget(p=new QPushButton(tr("Hosts...")),0); + connect(p,SIGNAL(clicked()),this,SLOT(editUserHosts())); + p->setEnabled(req->hasRole("getuserhosts")); + vl->addWidget(p=new QPushButton(tr("Roles...")),0); + connect(p,SIGNAL(clicked()),this,SLOT(editUserRoles())); + p->setEnabled(req->hasRole("getuseracl")); + vl->addStretch(10); + + //host tab + tab->addTab(hosttab=new QWidget,tr("Hosts")); + //status bar statusBar()->setSizeGripEnabled(true); @@ -125,6 +156,19 @@ MOverview::MOverview(MWebRequest*mw,QString pk) updateEvents(); }else{ eventtab->setEnabled(false); + tab->setTabEnabled(tab->indexOf(eventtab),false); + } + if(req->hasRole("getusers")){ + updateUsers(); + }else{ + usertab->setEnabled(false); + tab->setTabEnabled(tab->indexOf(usertab),false); + } + if(req->hasRole("gethosts")){ + updateHosts(); + }else{ + hosttab->setEnabled(false); + tab->setTabEnabled(tab->indexOf(hosttab),false); } } @@ -185,3 +229,63 @@ void MOverview::editEvent() ed.exec(); updateEvents(); } + +void MOverview::updateUsers() +{ + QListusl=req->getAllUsers(); + usermodel->clear(); + usermodel->insertColumns(0,2); + usermodel->insertRows(0,usl.size()); + usermodel->setHorizontalHeaderLabels(QStringList()<setData(usermodel->index(i,0),usl[i].userId()); + usermodel->setData(usermodel->index(i,1),usl[i].description()); + } + usertable->resizeColumnToContents(0); + usertable->resizeColumnToContents(1); +} + +void MOverview::newUser() +{ + //get name + QString name; + while(1){ + bool ok; + name=QInputDialog::getText(this,tr("New User"),tr("Please enter new user name (only letters, digits, and underscore allowed):"),QLineEdit::Normal,name,&ok); + if(!ok) + return; + if(QRegExp("[A-Za-z0-9_]+").exactMatch(name)) + break; + if(QMessageBox::warning(this,tr("Error"),tr("The user name must contain only letters, digits, and underscores and must be at least one character long!"),QMessageBox::Retry|QMessageBox::Abort)!=QMessageBox::Retry) + return; + } + //send request + MUser(req,name).create(); + //update display + updateUsers(); +} + +void MOverview::deleteUser(){} + +void MOverview::editUserDescription() +{ + //get selection + QModelIndex sel=usertable->currentIndex(); + if(!sel.isValid())return; + //get uname & descr + QString name=usermodel->data(usermodel->index(sel.row(),0)).toString(); + QString descr=usermodel->data(usermodel->index(sel.row(),1)).toString(); + //edit descr + bool ok; + descr=QInputDialog::getText(this,tr("Edit Description"),tr("Descriptionof user %1:").arg(name),QLineEdit::Normal,descr,&ok); + if(ok) + MUser(req,name).setDescription(descr); + //update + updateUsers(); +} + +void MOverview::editUserRoles(){} +void MOverview::editUserHosts(){} + + +void MOverview::updateHosts(){} diff --git a/src/overview.h b/src/overview.h index cd97875..dbd0f46 100644 --- a/src/overview.h +++ b/src/overview.h @@ -35,12 +35,29 @@ class MOverview:public QMainWindow private slots: /**try to log in again*/ void relogin(); + /**create a new event*/ void newEvent(); /**edit existing event*/ void editEvent(); /**update list of events*/ void updateEvents(); + + /**update list of users*/ + void updateUsers(); + /**create new user*/ + void newUser(); + /**delete selected user*/ + void deleteUser(); + /**set users description*/ + void editUserDescription(); + /**set users roles*/ + void editUserRoles(); + /**set users hosts*/ + void editUserHosts(); + + /**update list of hosts*/ + void updateHosts(); private: //my session object QPointerreq; @@ -48,9 +65,9 @@ class MOverview:public QMainWindow QString profilekey; //widgets QTabWidget*tab; - QWidget*eventtab,*carttab; - QTableView*eventtable; - QStandardItemModel*eventmodel; + QWidget*eventtab,*carttab,*usertab,*hosttab; + QTableView*eventtable,*usertable,*hosttable; + QStandardItemModel*eventmodel,*usermodel,*hostmodel; }; #endif diff --git a/src/smoke.pro b/src/smoke.pro index a09b10f..c0d7e71 100644 --- a/src/smoke.pro +++ b/src/smoke.pro @@ -31,7 +31,8 @@ SOURCES = \ overview.cpp \ eventedit.cpp \ event.cpp \ - room.cpp + room.cpp \ + user.cpp HEADERS = \ keygen.h \ mainwindow.h \ @@ -40,7 +41,8 @@ HEADERS = \ overview.h \ eventedit.h \ event.h \ - room.h + room.h \ + user.h RESOURCES += files.qrc diff --git a/src/webrequest.cpp b/src/webrequest.cpp index dd4da22..d9be3f4 100644 --- a/src/webrequest.cpp +++ b/src/webrequest.cpp @@ -60,8 +60,8 @@ bool MWebRequest::request(QString hreq,QByteArray data) hrh.setValue("X-MagicSmoke-Request",hreq); hrh.setValue("X-MagicSmoke-Session",sessionid); hrh.setContentLength(data.size()); - hrh.setContentType("application/x-magicsmoke"); - qDebug("Requesting data with:\n%s\n\n%s",hrh.toString().toAscii().data(),data.data()); + hrh.setContentType("application/x-magicsmoke; charset=UTF-8"); + qDebug("----------------------->\nRequesting data with:\n%s\n\nRequest Body:\n%s\n<---->",hrh.toString().toAscii().data(),data.data()); waitid=req.request(hrh,data); qDebug("started req %i",waitid); fin=false;errstr=""; @@ -96,8 +96,8 @@ bool MWebRequest::request(QString hreq,QByteArray data) rspdata=req.readAll(); rspstatus=rsph.value("x-magicsmoke-status").remove('"').trimmed().toLower(); // - qDebug("HTTP Response Headers:\n%s",rsph.toString().toAscii().data()); - qDebug("HTTP Response Body:\n%s\n<-->",rspdata.data()); + qDebug("----->\nHTTP Response Headers:\n%s",rsph.toString().toAscii().data()); + qDebug("HTTP Response Body:\n%s\n<------------------",rspdata.data()); return true; } @@ -344,6 +344,29 @@ QList MWebRequest::getAllRooms() return ret; } +QList MWebRequest::getAllUsers() +{ + errstr=""; + if(!request("getusers",""))return QList(); + //parse return document + QDomDocument doc; + QString msg;int ln,cl; + if(!doc.setContent(rspdata,&msg,&ln,&cl)){ + errstr=tr("Error parsing EventList XML data (line %1 column %2): %3").arg(ln).arg(cl).arg(msg); + return QList(); + } + QDomElement root=doc.documentElement(); + QDomNodeList nl=root.elementsByTagName("User"); + QListret; + for(int i=0;igetAllRooms(); + /**returns a list of all users**/ + QListgetAllUsers(); + public slots: /**set how long to wait for a web request*/ void setTimeout(int); diff --git a/www/inc/db_mysql.php b/www/inc/db_mysql.php index 8f6e2dd..7b4d17e 100644 --- a/www/inc/db_mysql.php +++ b/www/inc/db_mysql.php @@ -11,6 +11,7 @@ class MysqlEngine extends DbEngine private $dbhdl=false; private $dbname=""; private $engine="InnoDB"; + private $charset="utf8"; /**initialize driver*/ public function __construct($server,$user,$pass) @@ -40,11 +41,16 @@ class MysqlEngine extends DbEngine public function tryConnect() { + //connect $this->dbhdl=mysql_connect($this->server,$this->user,$this->pass); if($this->dbhdl===false) die("Unable to connect to database system. Giving up."); + //select DB if(mysql_query("use $this->dbname",$this->dbhdl)===false) die("Unable to select database \"$this->dbname\". Giving up."); + //TODO: select Unicode; TODO: fix it to be configurable + //if(mysql_query("SET NAMES 'utf8'")) + // die("cannot set character set to utf-8"); } public function haveTable($tnm) diff --git a/www/inc/session.php b/www/inc/session.php index cfcdf89..ddac732 100644 --- a/www/inc/session.php +++ b/www/inc/session.php @@ -2,7 +2,7 @@ // // PHP Implementation: session // -// Description: +// Description: implements session and ACL management // // // Author: Konrad Rosenbaum , (C) 2007 @@ -217,4 +217,101 @@ class Session } }; +//return all users to client +function getAllUsersXml() +{ + global $db; + header("X-MagicSmoke-Status: Ok"); + $res=$db->select("users","uname,description",""); + print("\n"); + for($i=0;$i".htmlentities($res[$i]["description"])."\n"); + } + print(""); +} + +//return the roles of a specific user +function getUserAclXml($user) +{ + //sanity check + $user=trim($user); + if(ereg("^[A-Za-z0-9_]+$",$user)===false){ + header("X-MagicSmoke-Status: SyntaxError"); + die("invalid user name"); + } + //go on... + global $db,$ALLOWEDREQUESTS; + header("X-MagicSmoke-Status: Ok"); + //create list of roles + $roles=$ALLOWEDREQUESTS; + $roles[]="_admin"; + //get roles from DB + $res=$db->select("userrole","role","uname=".$user); + $acl=array(); + foreach($res as $rl)$acl[]=$rl["role"]; + print("\n"); + foreach($roles as $rl){ + print("\n"); + } + print("\n"); +} + +//helper function: parse User-XML-structure +function parseUserXml($txt) +{ + $xml=new DOMDocument; + if(!$xml->loadXML($txt)){ + header("X-MagicSmoke-Status: SyntaxError"); + die("unable to parse XML data"); + } + $ret=array(); + foreach($xml->getElementsByTagName("User") as $el){ + $usr["name"]=$el->getAttribute("name"); + $usr["descr"]=""; + foreach($el->childNodes as $cn) + if($cn->nodeType==XML_TEXT_NODE) + $usr["descr"]=$cn->wholeText; + $ret[]=$usr; + } + return $ret; +} + +//set new description for user +function setUserDescrXml($txt) +{ + global $db; + $usr=parseUserXml($txt); + for($i=0;$iupdate("users",array("description"=>$usr[$i]["descr"]),"uname=".$db->escapeString($usr[$i]["name"])); + } + header("X-MagicSmoke-Status: Ok"); +} + +//add a new user +function addUserXml($txt) +{ + global $db; + $usr=parseUserXml($txt); + header("X-MagicSmoke-Status: Ok"); + print("\n"); + for($i=0;$ibeginTransaction(); + $res=$db->select("users","uname","uname='".$usr[$i]["name"]."'"); + if(count($res)==0){ + //create new + $db->insert("users",array("uname"=>$usr[$i]["name"],"description"=>$usr[$i]["descr"])); + //print data + print("".htmlentities($usr[$i]["descr"])."\n"); + } + $db->commitTransaction(); + } + print("\n"); +} + ?> \ No newline at end of file diff --git a/www/machine.php b/www/machine.php index d2b28b1..cf024a2 100644 --- a/www/machine.php +++ b/www/machine.php @@ -14,8 +14,10 @@ $ALLOWEDREQUESTS=array( "serverinfo", //info request "startsession","sessionauth","closesession", //session requests //all requests below here need authentication - "getmyroles", //role management + "getmyroles", //role management: get my own ACLs //all requests below here need a role entry in the DB + "getusers","setuserdescription","getuseracl","setuseracl","getuserhosts","setuserhosts","adduser","deleteuser",//user management + "gethosts","sethost","addhost","deletehost", //host management "geteventlist", "geteventdata", "seteventdata", //event infos "getroomdata","setroomdata",//room infos "getcustomerlist" //customer info @@ -32,6 +34,12 @@ if(isset($HTTP_RAW_POST_DATA)){ $REQUESTDATA=$HTTP_RAW_POST_DATA; } +/**provided to encode data into UTF-8 for transport*/ +function xmlentities($str) +{ + return htmlentities($str); +} + //initialize basics include("inc/loader.php"); @@ -137,6 +145,39 @@ if($SMOKEREQUEST=="setroomdata"){ exit(); } +//get all users +if($SMOKEREQUEST=="getusers"){ + getAllUsersXml(); + exit(); +} + +if($SMOKEREQUEST=="setuserdescription"){ + setUserDescrXml($REQUESTDATA); + exit(); +} + +//get ACL info of specific users +if($SMOKEREQUEST=="getuseracl"){ + getUserAclXml($REQUESTDATA); + exit(); +} + +if($SMOKEREQUEST=="setuseracl"){} +if($SMOKEREQUEST=="getuserhosts"){} +if($SMOKEREQUEST=="setuserhosts"){} + +if($SMOKEREQUEST=="adduser"){ + addUserXml($REQUESTDATA); + exit(); +} + +if($SMOKEREQUEST=="deleteuser"){} +if($SMOKEREQUEST=="gethosts"){} +if($SMOKEREQUEST=="sethost"){} +if($SMOKEREQUEST=="addhost"){} +if($SMOKEREQUEST=="deletehost"){} + + //EOF header("X-MagicSmoke-Status: Error"); die("Internal Error: unknown command, hiccup in code structure.");