Newer
Older
/*******************************************************************************
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
class AuthDB : public libhttppp::HttpEvent{
public:
AuthDB(authdb::AuthBackend &backend,netplus::socket *ssock) : HttpEvent(ssock), _Backend(backend){
_Indexpage.loadFile(_IndexElement,"../data/index.html");
std::ifstream authfs("../data/authdb.js", std::ios::ate);
size_t asize = authfs.tellg();
_AuthJs.resize(asize);
authfs.seekg(0);
authfs.read(_AuthJs.data(),asize);
void listUsers(libhttppp::HttpRequest *curreq, const int tid, ULONG_PTR args){
size_t rd=sizeof(authdb::AuthHeader),end=_Backend.end();
libhtmlpp::HtmlElement *content=root.getElementbyID("content");
libhtmlpp::HtmlElement *domain=root.getElementbyID("domain");
userlist <<"<div><p>UserList:</p><table class=\"userlist\">";
_Backend.read((unsigned char*)&cur,sizeof(AuthData::Record));
if(cur.type == UserData && strcmp(cur.fieldname,"username")==0){
class UserData udat(cur.uuid);
user.info(_Backend,udat,upos);
char cruid[20];
uuid_unparse(cur.uuid,cruid);
userlist << "<tr><td class=\"list_username\">"
<< udat.getUsername()
<< "</td></tr>"
<< "<tr><td><img class=\"list_picture\" style=\"height:100px;\" src=\"/admin/getavatar/" << cruid << ".jpg\" alt=\"avatar\">"
<< "</li>"
<< "<li><span>Firstname:</span><span>" << udat.getFirstname() << "</span></li>"
<< "<li><span>Lastname:</span><span>" << udat.getLastname() << "</span></li>"
<< "<li><span>Email:</span><span>" << udat.getMail() << "</span></li>"
<< "</ul></td><td><ul class=\"usertoolbar\" >"
<< "<li><a class=\"button\" href=\"/admin/edituser/" << cruid << "\">EditUser</a></li>"
<< "<li><a class=\"button\" href=\"/admin/removeuser/" << cruid << "\">RemoveUser</a></li>"
<< "</ul></td></tr>";
}catch(AuthBackendError &e){
std::cerr << e.what() << std::endl;
dform << "<form>"
<< "<label for=\"domain_selector\">Select Domain:</label>"
<< "<select id=\"domain_selector\" name=\"domain_selector\">"
<< "<option value=\"admin.local\" >admin.local</option>"
<< "<option value=\"tuxist.de\" >tuxist.de</option>"
<< "</select>"
<< "</form>";
userlist << "</table></div><div><ul class=\"usertoolbar\" ><li><a class=\"button\" href=\"/admin/createuser\">CreateUser</a></li></ul></div>";
content->insertChild(userlist.parse());
libhtmlpp::print(&root,out,true);
rep.setContentType("text/html");
rep.send(curreq,out.c_str(),out.size());
}
void createUser(libhttppp::HttpRequest *curreq, const int tid, ULONG_PTR args){
libhttppp::HttpResponse rep;
libhttppp::HttpForm curform;
curform.parse(curreq);
if (curform.getBoundary()) {
uuid_t uid;
uuid_generate(uid);
for (libhttppp::HttpForm::MultipartForm::Data* curformdat = curform.MultipartFormData.getFormData();
curformdat; curformdat = curformdat->nextData()) {
if(curformdat->Value.empty())
continue;
std::string name;
int stortype=TextStorage;
for(libhttppp::HttpForm::MultipartForm::Data::ContentDisposition *curdispo=curformdat->getDisposition();
curdispo; curdispo=curdispo->nextContentDisposition()
){
std::string tmp=curdispo->getKey();
std::transform(tmp.begin(), tmp.end(), tmp.begin(),
[](unsigned char c){ return std::tolower(c); });
if(tmp=="name")
name=curdispo->getValue();
if(tmp=="filename"){
stortype=BinaryStorage;
}
if(name.empty() || stortype == EmptyStorage)
continue;
struct AuthData::Record cudat;
memset(&cudat,0,sizeof(AuthData::Record));
cudat.start=0xFE;
uuid_copy(cudat.uuid,uid);
cudat.type=UserData;
cudat.storage=stortype;
strcpy(cudat.fieldname,name.c_str());
if(cudat.storage==BinaryStorage){
cudat.datasize=curformdat->Value.size();
cudat.data=new char[cudat.datasize];
memcpy(cudat.data,curformdat->Value.data(),
cudat.datasize);
}else{
if(strcmp(cudat.fieldname,"pwhash")==0){
Hash hash;
std::string pwhash,inpw;
std::copy(curformdat->Value.begin(),curformdat->Value.end(),
std::back_inserter(inpw));
hash.hash(inpw,pwhash);
cudat.datasize=pwhash.length()+1;
cudat.data=new char[cudat.datasize];
memcpy(cudat.data,pwhash.c_str(),pwhash.length());
cudat.data[pwhash.length()]='\0';
}else{
cudat.datasize=curformdat->Value.size()+1;
cudat.data=new char[cudat.datasize];
memcpy(cudat.data,curformdat->Value.data(),
curformdat->Value.size());
cudat.data[curformdat->Value.size()]='\0';
}
}
udat.Data->append(cudat);
delete[] cudat.data;
}
User user;
user.create(_Backend,&udat);
std::cout << "user created!" << std::endl;
}
libhtmlpp::HtmlString form,out;
form << "<form method=\"post\" enctype=\"multipart/form-data\">"
<< "<table>"
<< "<tr><td><label for=\"username\">username:</label></td>"
<< "<td><input type=\"text\" name=\"username\"></td></tr>"
<< "<tr><td><label for=\"firstname\">firstname:</label></td>"
<< "<td><input type=\"text\" name=\"firstname\"></td></tr>"
<< "<tr><td><label for=\"lastname\">lastname:</label></td>"
<< "<td><input type=\"text\" name=\"lastname\"></td></tr>"
<< "<tr><td><label for=\"mail\">mail:</label></td>"
<< "<td><input type=\"text\" name=\"mail\"></td></tr>"
<< "<tr><td><label for=\"pwhash\">password:</label></td>"
<< "<td><input type=\"text\" name=\"pwhash\"></td></tr>"
<< "<tr><td><label for=\"avatar\">ProfilBild:</label></td>"
<< "<td><input type=\"file\" name=\"avatar\"></td></tr>"
<< "<tr><td></td><td><input type=\"submit\"></td></tr>"
<< "</table>"
<< "</form";
libhtmlpp::HtmlElement *content=root.getElementbyID("content");
content->insertChild(form.parse());
libhtmlpp::print(&root,out,true);
rep.setContentType("text/html");
rep.setContentLength(out.size());
rep.send(curreq,out.c_str(),out.size());
}
void removeuser(libhttppp::HttpRequest *curreq, const int tid, ULONG_PTR args){
char uid[255];
sscanf(curreq->getRequestURL(),"/admin/removeuser/%s",uid);
User user;
uuid_t uuid;
uuid_parse(uid,uuid);
user.remove(_Backend,uuid);
libhttppp::HttpResponse rep;
rep.setState(HTTP307);
rep.setVersion(HTTPVERSION(1.1));
rep.setHeaderData("Location")->push_back("/admin/listusers");
rep.setContentType("text/html");
rep.send(curreq,nullptr,0);
void getAvatar(libhttppp::HttpRequest *curreq, const int tid, ULONG_PTR args){
libhttppp::HttpResponse rep;
char cuid[255],ext[16];
sscanf(curreq->getRequestURL(),"/admin/getavatar/%[^.].%s",cuid,ext);
size_t rd=sizeof(authdb::AuthHeader),end=_Backend.end();
while(rd<end){
_Backend.read((unsigned char*)&cur,sizeof(AuthData::Record));
rd=_Backend.getPos()+cur.datasize;
if(uuid_compare(cur.uuid,uid)==0 && strcmp(cur.fieldname,"avatar")==0){
cur.data = new char[cur.datasize];
_Backend.read((unsigned char*)cur.data,cur.datasize);
char ctype[255];
snprintf(ctype,255,"image/%s",ext);
rep.setContentType(ctype);
}
}
if(end!=0){
rep.setState(HTTP404);
rep.send(curreq,nullptr,0);
}
};
void editUser(libhttppp::HttpRequest *curreq, const int tid, ULONG_PTR args){
size_t rd=sizeof(authdb::AuthHeader),end=_Backend.end();
char cuid[255];
sscanf(curreq->getRequestURL(),"/admin/edituser/%s",cuid);
uuid_t uid;
uuid_parse(cuid,uid);
AuthData editrec(uid);
bool data = false;
libhttppp::HttpForm curform;
curform.parse(curreq);
for (libhttppp::HttpForm::MultipartForm::Data* curformdat = curform.MultipartFormData.getFormData();
curformdat; curformdat = curformdat->nextData()) {
for(libhttppp::HttpForm::MultipartForm::Data::ContentDisposition *curdispo=curformdat->getDisposition();
curdispo; curdispo=curdispo->nextContentDisposition()
){
if(curformdat->Value.empty() || !curdispo->getValue())
continue;
data = true;
curec.Data->type=UserData;
curec.Data->storage=TextStorage;
for(libhttppp::HttpForm::MultipartForm::Data::ContentDisposition *curdispo=curformdat->getDisposition();
curdispo; curdispo=curdispo->nextContentDisposition()
){
std::string key;
if(curdispo->getKey())
key=curdispo->getKey();
std::transform(key.begin(), key.end(), key.begin(),
[](unsigned char c){ return std::tolower(c); });
if(key=="name"){
if(curec.Data->storage==TextStorage &&
strcmp(curec.Data->fieldname,"pwhash")==0){
Hash hash;
std::string pwhash,inpw;
std::copy(curformdat->Value.begin(),curformdat->Value.end(),
std::back_inserter(inpw));
hash.hash(inpw,pwhash);
curec.Data->datasize=pwhash.length()+1;
curec.Data->data=new char[curec.Data->datasize];
memcpy(curec.Data->data,pwhash.c_str(),pwhash.length());
curec.Data->data[pwhash.length()]='\0';
}else{
curec.Data->datasize=curformdat->Value.size()+1;
curec.Data->data=new char[curec.Data->datasize];
memcpy(curec.Data->data,curformdat->Value.data(),
curformdat->Value.size());
curec.Data->data[curformdat->Value.size()]='\0';
}
libhttppp::HttpResponse rep;
rep.setState(HTTP307);
rep.setVersion(HTTPVERSION(1.1));
rep.setHeaderData("Location")->push_back("/admin/listusers");
rep.setContentType("text/html");
rep.send(curreq,nullptr,0);
return;
}
libhtmlpp::HtmlPage page;
libhtmlpp::HtmlString form,out;
form << "<form method=\"post\" enctype=\"multipart/form-data\">"
<< "<table>";
while(rd<end){
_Backend.read((unsigned char*)&cur,sizeof(AuthData::Record));
rd=_Backend.getPos()+cur.datasize;
if(uuid_compare(cur.uuid,uid)==0 && cur.type==UserData && cur.storage==TextStorage){
cur.data = new char[cur.datasize];
_Backend.read((unsigned char*)cur.data,cur.datasize);
form << "<tr><td><label for=\"username\">" << cur.fieldname << "</label></td>"
<< "<td><input type=\"text\" name=\"" << cur.fieldname << "\" value=\""
<< cur.data <<"\"></td></tr>";
}catch(AuthBackendError &e){
std::cerr << e.what() << std::endl;
}
}
}
form << "<tr><td><label for=\"avatar\">ProfilBild:</label></td>"
<< "<td><input type=\"file\" name=\"avatar\"></td></tr>"
<< "<tr><td></td><td><input type=\"submit\"></td></tr>"
<< "</table>"
<< "</form";
libhtmlpp::HtmlElement root=_IndexElement;
libhtmlpp::HtmlElement *content=root.getElementbyID("content");
content->insertChild(form.parse());
libhtmlpp::print(&root,out,true);
libhttppp::HttpResponse rep;
rep.setContentType("text/html");
rep.setContentLength(out.size());
rep.send(curreq,out.c_str(),out.size());
}
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
void listDomains(libhttppp::HttpRequest *curreq, const int tid, ULONG_PTR args){
libhtmlpp::HtmlString list,out;
libhtmlpp::HtmlElement root=_IndexElement;
libhtmlpp::HtmlElement *content=root.getElementbyID("content");
list << "<div><ul class=\"usertoolbar\" >"
<< "<li><a class=\"button\" href=\"/admin/createdomain\">CreateDomain</a>"
<< "</li></ul></div>";
content->insertChild(list.parse());
libhtmlpp::print(&root,out,true);
libhttppp::HttpResponse rep;
rep.setContentType("text/html");
rep.setContentLength(out.size());
rep.send(curreq,out.c_str(),out.size());
}
void createDomain(libhttppp::HttpRequest *curreq, const int tid, ULONG_PTR args){
libhtmlpp::HtmlString form,out;
libhtmlpp::HtmlElement root=_IndexElement;
libhtmlpp::HtmlElement *content=root.getElementbyID("content");
form << "<form method=\"post\" enctype=\"multipart/form-data\">"
<< "<table>"
<< "<tr><td><label for=\"domainname\">Domain Name:</label></td>"
<< "<td><input class=\"domaintext\" type=\"text\" name=\"domainname\" value=\"tuxist.de\"></td></tr>"
<< "<tr><td><label for=\"domainstorage\">Domain Storage:</label></td>"
<< "<td><select name=\"domainstorage\">"
<< "<option value=\"file\">File</option>"
<< "</select></td></tr>"
<< "<tr><td><label for=\"domainname\">Domain Options:</label></td>"
<< "<td><input class=\"domaintext\" type=\"text\" name=\"options\" value=\"/var/lib/authdb/tuxist.de.db\"></td></tr>"
<< "<tr><td></td><td><input type=\"submit\" value=\"save\" ></td></tr>"
<< "</table>";
content->insertChild(form.parse());
libhtmlpp::print(&root,out,true);
libhttppp::HttpResponse rep;
rep.setContentType("text/html");
rep.setContentLength(out.size());
rep.send(curreq,out.c_str(),out.size());
}
void AdminController(libhttppp::HttpRequest *curreq, const int tid, ULONG_PTR args){
try {
if(strncmp(curreq->getRequestURL(),"/admin/listusers",16)==0){
listUsers(curreq,tid,args);
}else if(strncmp(curreq->getRequestURL(),"/admin/createuser",17)==0){
createUser(curreq,tid,args);
}else if(strncmp(curreq->getRequestURL(),"/admin/removeuser",17)==0){
removeuser(curreq,tid,args);
}else if(strncmp(curreq->getRequestURL(),"/admin/edituser",15)==0){
editUser(curreq,tid,args);
}else if(strncmp(curreq->getRequestURL(),"/admin/getavatar/",17)==0){
getAvatar(curreq,tid,args);
}else if(strncmp(curreq->getRequestURL(),"/admin/listdomains/",18)==0){
listDomains(curreq,tid,args);
}else if(strncmp(curreq->getRequestURL(),"/admin/createdomain/",19)==0){
createDomain(curreq,tid,args);
}else if(strncmp(curreq->getRequestURL(),"/admin/authdb.js",16)==0){
libhttppp::HttpResponse rep;
rep.setContentType("text/javascript");
rep.send(curreq,_AuthJs.data(),_AuthJs.size());
}else{
libhtmlpp::HtmlElement root=_IndexElement;
libhtmlpp::HtmlString out;
libhtmlpp::print(&root,out,true);
libhttppp::HttpResponse rep;
rep.setContentType("text/html");
rep.setContentLength(out.size());
rep.send(curreq,out.c_str(),out.size());
}
}catch(AuthBackendError &e){
libhttppp::HttpResponse rep;
rep.setState(HTTP500);
rep.setContentType("text/html");
rep.send(curreq,e.what(),strlen(e.what()));
}
void RequestEvent(libhttppp::HttpRequest *curreq, const int tid, ULONG_PTR args){
if(strncmp(curreq->getRequestURL(),"/admin",6)==0){
AdminController(curreq,tid,args);
}else if(strncmp(curreq->getRequestURL(),"/api",4)==0){
}else{
libhttppp::HttpResponse rep;
rep.setState(HTTP307);
rep.setVersion(HTTPVERSION(1.1));
rep.setHeaderData("Location")->push_back("/admin");
rep.setContentType("text/html");
rep.send(curreq,nullptr,0);
libhtmlpp::HtmlElement _IndexElement;
libhtmlpp::HtmlPage _Indexpage;
};
int searchValue(authdb::AuthBackend &backend,const char*fieldname,const char *value){
int rd=sizeof(authdb::AuthHeader),brd=rd;
while(rd>backend.end()){
backend.setPos(rd);
backend.read((unsigned char*)user,sizeof(AuthData::Record));
user->data = new char[user->datasize];
backend.read((unsigned char*)user->data,user->datasize);
Data->storage=EmptyStorage;
uuid_copy(Data->uuid,id);
AuthData::AuthData(const AuthData &src){
Data = new AuthData::Record;
AuthData *dest=this;
for(const AuthData *cursrc=&src; cursrc; cursrc=cursrc->next()){
memcpy(dest->Data,cursrc->Data,sizeof(AuthData::Record));
Data->data=new char[cursrc->Data->datasize];
memcpy(dest->Data->data,cursrc->Data->data,cursrc->Data->datasize);
if(cursrc->next()){
dest->_next=new AuthData(this->Data->uuid);
dest=dest->_next;
}
}
AuthData *AuthData::append(const AuthData &src){
AuthData *curec=this;
curec=curec->_next;
break;
}
curec=curec->_next;
for(const AuthData *cuxrcrec=&src; cuxrcrec; cuxrcrec=cuxrcrec->next()){
memcpy(curec->Data,cuxrcrec->Data,sizeof(AuthData::Record));
curec->Data->data=new char[curec->Data->datasize];
memcpy(curec->Data->data,cuxrcrec->Data->data,curec->Data->datasize);
curec->_next=nullptr;
if(cuxrcrec->next()){
curec->_next=new AuthData(src.Data->uuid);
}
return curec;
}
AuthData *AuthData::append(const AuthData::Record &src){
AuthData *curec=this;
curec=curec->_next;
break;
}
curec=curec->_next;
memcpy(curec->Data,&src,sizeof(AuthData::Record));
curec->Data->data=new char[curec->Data->datasize];
memcpy(curec->Data->data,src.data,curec->Data->datasize);
bool getRecord(authdb::AuthBackend &backend,AuthData &rec,int type){
bool found=false;
int rd=sizeof(authdb::AuthHeader),end=backend.end();
AuthData::Record cur;
memset(&cur,0,sizeof(AuthData::Record));
backend.setPos(rd);
backend.read((unsigned char*)&cur,sizeof(AuthData::Record));
rd=backend.getPos()+cur.datasize;
if(uuid_compare(cur.uuid,rec.Data->uuid) == 0 && cur.type == type){
cur.data=new char[cur.datasize];
backend.read((unsigned char*)cur.data,cur.datasize);
rec.append(cur);
delete[] cur.data;
bool editRecord(AuthBackend &backend,AuthData &rec,int type){
AuthData old(rec.Data->uuid);
std::vector<AuthData::Record*> changemap; //use fieldname;
std::vector<AuthData::Record*> newmap;
for(const AuthData *curdat=&rec; curdat; curdat=curdat->_next){
for(const AuthData *olddat=&old; olddat; olddat=olddat->_next){
if(uuid_compare(olddat->Data->uuid,curdat->Data->uuid)==0
&& strcmp(olddat->Data->fieldname,curdat->Data->fieldname)==0
){
size_t end = olddat->Data->datasize;
if(end!=curdat->Data->datasize){
changemap.push_back(curdat->Data);
}else{
for(size_t i=0; i<end; ++i){
if(olddat->Data->data[i]!=curdat->Data->data[i]){
changemap.push_back(curdat->Data);
break;
}
}
}
newentry=false;
}
}
if(newentry){
newmap.push_back(curdat->Data);
}
}
for(auto newel : newmap){
backend.setPos(backend.end());
backend.write((unsigned char*)newel,sizeof(AuthData::Record));;
backend.write((unsigned char*)newel->data,newel->datasize);
}
const unsigned char zero = 0;
for(auto chel : changemap){
size_t rd=sizeof(authdb::AuthHeader),end=backend.end();
while(rd<end){
authdb::AuthData::Record *rdrec=new AuthData::Record;
backend.setPos(rd);
backend.read((unsigned char*)rdrec,sizeof(AuthData::Record));
if(uuid_compare(rdrec->uuid,rec.Data->uuid)==0 && strcmp(rdrec->fieldname,chel->fieldname)==0){
backend.setPos(backend.end());
backend.write((const unsigned char*)chel,sizeof(AuthData::Record));
backend.write((const unsigned char*)chel->data,chel->datasize);
size_t dsize=rdrec->datasize;
backend.setPos(rd);
backend.write((const unsigned char*)rdrec,sizeof(AuthData::Record));
for(size_t i =0; i<rdrec->datasize; ++i){
backend.write(&zero,1);
}
rd=backend.getPos();
}else{
rd=backend.getPos()+rdrec->datasize;
}
delete rdrec;
}
}
if(!changemap.empty() || !newmap.empty())
return true;
return false;
}
void delRecord(AuthBackend &backend,const uuid_t uid){
size_t end=backend.end(),rd=sizeof(AuthHeader);
if(!uid)
throw AuthBackendError("user info uid required!");
while(rd<end){
authdb::AuthData::Record *cur=new AuthData::Record;
backend.setPos(rd);
backend.read((unsigned char*)cur,sizeof(AuthData::Record));
backend.setPos(wr);
backend.write((unsigned char*)cur,sizeof(AuthData::Record));
const unsigned char zero=0;
for(size_t i=0; i<dsize; ++i){
backend.write(&zero,1);
}
}
delete cur;
}
}
int main(int argc, char *argv[]){
cmdplus::CmdController &cmd=cmdplus::CmdController::getInstance();
if(!cmd.checkRequired()){
std::cerr << "Config Path required !" << std::endl;
cmd.printHelp();
return -1;
}
confplus::Config config(cmd.getCmdbyKey("config")->getValue());
authdb::AuthBackend backend(authdb::AuthBackendType::File,
config.getValue(config.getKey("/AUTHDB/ADMINDB/PATH"),0),
"admin.local"
);
libhttppp::HttpD httpd(
config.getValue(config.getKey("/AUTHDB/BIND"),0),
config.getIntValue(config.getKey("/AUTHDB/PORT"),0),
config.getIntValue(config.getKey("/AUTHDB/MAXCONN"),0),
nullptr,
nullptr
);
authdb::AuthDB authdb(backend,httpd.getServerSocket());
}catch(authdb::AuthBackendError &e){
std::cerr << e.what() << std::endl;
}