Skip to content
Snippets Groups Projects
authdb.cpp 12.7 KiB
Newer Older
jan.koester's avatar
jan.koester committed
/*******************************************************************************
jan.koester's avatar
jan.koester committed
 * Copyright (c) 2025, Jan Koester jan.koester@gmx.net
jan.koester's avatar
jan.koester committed
 * 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.
 *******************************************************************************/

#include <iostream>
jan.koester's avatar
jan.koester committed
#include <cstring>
jan.koester's avatar
jan.koester committed
#include <sstream>
jan.koester's avatar
jan.koester committed

jan.koester's avatar
jan.koester committed
#include <uuid/uuid.h>
jan.koester's avatar
jan.koester committed
#include <cmdplus/cmdplus.h>
#include <confplus/conf.h>
jan.koester's avatar
jan.koester committed

jan.koester's avatar
jan.koester committed
#include <httppp/httpd.h>
jan.koester's avatar
jan.koester committed
#include <htmlpp/html.h>
jan.koester's avatar
jan.koester committed

jan.koester's avatar
jan.koester committed
#include "backend.h"
jan.koester's avatar
jan.koester committed
#include "authdb.h"
jan.koester's avatar
jan.koester committed
#include "user.h"
jan.koester's avatar
jan.koester committed
#include "types.h"
jan.koester's avatar
jan.koester committed

jan.koester's avatar
jan.koester committed
namespace authdb {
jan.koester's avatar
jan.koester committed

    bool initalized=true;

jan.koester's avatar
jan.koester committed
    class AuthDB : public libhttppp::HttpEvent{
    public:
        AuthDB(authdb::AuthBackend &backend,netplus::socket *ssock) : HttpEvent(ssock), _Backend(backend){

        };

jan.koester's avatar
jan.koester committed
        void listUsers(libhttppp::HttpRequest *curreq, const int tid, ULONG_PTR args){
            _Backend.lock();

jan.koester's avatar
jan.koester committed
            authdb::AuthData *cur=new AuthData;
jan.koester's avatar
jan.koester committed

            size_t rd=sizeof(authdb::AuthHeader),end=_Backend.end();

            _Backend.setPos(rd);

jan.koester's avatar
jan.koester committed


            libhtmlpp::HtmlElement root;
            libhtmlpp::HtmlPage page;

            page.loadFile(root,"../data/index.html");

            libhtmlpp::HtmlString userlist,out;

            libhtmlpp::HtmlElement *content=root.getElementbyID("content");



            userlist <<"<div><p>UserList:</p>";
jan.koester's avatar
jan.koester committed

            while(rd<end){
jan.koester's avatar
jan.koester committed
                std::cout << rd << std::endl;
jan.koester's avatar
jan.koester committed
                _Backend.ReadAuthData(cur,rd);
                rd=_Backend.getPos();
jan.koester's avatar
jan.koester committed
                if(strcmp(cur->fieldname,"username")==0){
jan.koester's avatar
jan.koester committed
                    AuthDataRecord *rec=new AuthDataRecord;
jan.koester's avatar
jan.koester committed
                    uuid_t uid;
jan.koester's avatar
jan.koester committed
                    uuid_parse(cur->uuid,uid);
jan.koester's avatar
jan.koester committed
                    _Backend.setPos(sizeof(AuthHeader));
jan.koester's avatar
jan.koester committed
                    getRecord(_Backend,rec,uid,DataType::UserData);
jan.koester's avatar
jan.koester committed
                    userlist+="User: ";
jan.koester's avatar
jan.koester committed
                    std::copy(cur->data,cur->data+cur->datasize,std::back_inserter(userlist));
jan.koester's avatar
jan.koester committed
                    userlist+=" UID: ";
jan.koester's avatar
jan.koester committed
                    userlist+= cur->uuid;
jan.koester's avatar
jan.koester committed
                    userlist+= "<br>";
                    for(AuthDataRecord *curec=rec; curec; curec=curec->next()){
jan.koester's avatar
jan.koester committed
                        if(strcmp(curec->Data->fieldname,"username")!=0 && curec->Data->data){
jan.koester's avatar
jan.koester committed
                            userlist+="  ";
jan.koester's avatar
jan.koester committed
                            userlist+=curec->Data->fieldname;
jan.koester's avatar
jan.koester committed
                            userlist+=": ";
jan.koester's avatar
jan.koester committed
                            std::copy(curec->Data->data,curec->Data->data+curec->Data->datasize,
jan.koester's avatar
jan.koester committed
                                      std::back_inserter(userlist));
jan.koester's avatar
jan.koester committed
                            userlist+= "<br>";
jan.koester's avatar
jan.koester committed
                    delete rec;
jan.koester's avatar
jan.koester committed
                }
jan.koester's avatar
jan.koester committed
            }
jan.koester's avatar
jan.koester committed
            userlist << "</div><div><ul><li><a class=\"button\" href=\"/admin/createuser\">CreateUser</a></li></ul></div>";

jan.koester's avatar
jan.koester committed
            delete cur;

jan.koester's avatar
jan.koester committed
            _Backend.unlock();
jan.koester's avatar
jan.koester committed
            content->insertChild(userlist.parse());

            libhtmlpp::print(&root,out,true);

jan.koester's avatar
jan.koester committed
            libhttppp::HttpResponse rep;
jan.koester's avatar
jan.koester committed
            rep.setContentType("text/html");
            rep.send(curreq,out.c_str(),out.size());
jan.koester's avatar
jan.koester committed
        }

        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);
jan.koester's avatar
jan.koester committed
                class UserData udat(uid);
jan.koester's avatar
jan.koester committed

                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()
                    ){
jan.koester's avatar
jan.koester committed
                        if(curformdat->Value.empty() || !curdispo->getValue())
                            continue;

jan.koester's avatar
jan.koester committed
                        std::vector<char> tmp;
                        if(strcmp(curdispo->getValue(),"avatar")!=0){
                            std:copy(curformdat->Value.begin(),curformdat->Value.end(),std::back_inserter(tmp));
                            tmp.push_back('\0');
                        }

jan.koester's avatar
jan.koester committed
                        if(strcmp(curdispo->getValue(),"username")==0){
jan.koester's avatar
jan.koester committed
                            udat.setUserName(tmp.data());
jan.koester's avatar
jan.koester committed
                        }else if(strcmp(curdispo->getValue(),"firstname")==0){
jan.koester's avatar
jan.koester committed
                            udat.setFirstName(tmp.data());
jan.koester's avatar
jan.koester committed
                        }else if(strcmp(curdispo->getValue(),"lastname")==0){
jan.koester's avatar
jan.koester committed
                            udat.setLastName(tmp.data());
jan.koester's avatar
jan.koester committed
                        }else if(strcmp(curdispo->getValue(),"mail")==0){
jan.koester's avatar
jan.koester committed
                            udat.setMail(tmp.data());
jan.koester's avatar
jan.koester committed
                        }else if(strcmp(curdispo->getValue(),"avatar")==0 && !curformdat->Value.empty()){
jan.koester's avatar
jan.koester committed
                            udat.setAvatar(curformdat->Value);
                        }
                    }
                }

                User user;
                user.create(_Backend,&udat);
                std::cout << "user created!" << std::endl;
            }

jan.koester's avatar
jan.koester committed
            libhtmlpp::HtmlElement root;
jan.koester's avatar
jan.koester committed
            libhtmlpp::HtmlPage page;

jan.koester's avatar
jan.koester committed
            page.loadFile(root,"../data/index.html");

            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=\"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);
jan.koester's avatar
jan.koester committed

            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){
            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);
jan.koester's avatar
jan.koester committed
            }else{
                libhtmlpp::HtmlElement root;
                libhtmlpp::HtmlPage page;
                page.loadFile(root,"../data/index.html");
                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());
jan.koester's avatar
jan.koester committed
            }
        }

        void RequestEvent(libhttppp::HttpRequest *curreq, const int tid, ULONG_PTR args){
            if(strncmp(curreq->getRequestURL(),"/admin",6)==0){
                AdminController(curreq,tid,args);
jan.koester's avatar
jan.koester committed
            }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);
jan.koester's avatar
jan.koester committed
            }
jan.koester's avatar
jan.koester committed
        };
    private:
        authdb::AuthBackend &_Backend;
    };

    int searchValue(authdb::AuthBackend &backend,const char*fieldname,const char *value){
jan.koester's avatar
jan.koester committed
        authdb::AuthData *user=new AuthData;
jan.koester's avatar
jan.koester committed
        int rd=sizeof(authdb::AuthHeader),brd=rd;
        while(rd>backend.end()){
            backend.ReadAuthData(user,rd);
            rd=backend.getPos();
jan.koester's avatar
jan.koester committed
            if(strcmp(user->fieldname,fieldname) == 0){
                if(strcmp(user->data,value) == 0){
                    delete[] user->data;
                    delete user;
jan.koester's avatar
jan.koester committed
                    return brd;
                }
            }
            brd=rd;
jan.koester's avatar
jan.koester committed
            delete[] user->data;
jan.koester's avatar
jan.koester committed
        }
jan.koester's avatar
jan.koester committed
        delete user;
jan.koester's avatar
jan.koester committed
        return -1;
    }

    AuthDataRecord::AuthDataRecord(){
jan.koester's avatar
jan.koester committed
         Data = new AuthData;
jan.koester's avatar
jan.koester committed
         memset((void*)Data,0,sizeof(AuthData));
jan.koester's avatar
jan.koester committed
         Data->start=0xFE;
         Data->end=0xFF;
jan.koester's avatar
jan.koester committed
        _next=nullptr;
    }

    AuthDataRecord::AuthDataRecord(const AuthDataRecord &src){
jan.koester's avatar
jan.koester committed
        Data = new AuthData;
jan.koester's avatar
jan.koester committed
        memcpy(&Data,&src.Data,sizeof(src.Data));
jan.koester's avatar
jan.koester committed
        memcpy(Data->data,src.Data->data,src.Data->datasize);
jan.koester's avatar
jan.koester committed
        _next=nullptr;
    }

    AuthDataRecord::~AuthDataRecord(){
jan.koester's avatar
jan.koester committed
        delete Data;
jan.koester's avatar
jan.koester committed
    }

    AuthDataRecord *AuthDataRecord::next(){
jan.koester's avatar
jan.koester committed
        return _next;
jan.koester's avatar
jan.koester committed
    };


    bool getRecord(authdb::AuthBackend &backend,AuthDataRecord *dest,uuid_t id,int type){
        bool found=false;
jan.koester's avatar
jan.koester committed
        AuthData *cur=new AuthData;
jan.koester's avatar
jan.koester committed
        int rd=sizeof(authdb::AuthHeader),end=backend.end();

        char plainid[255];

        uuid_unparse(id,plainid);

        AuthDataRecord *curec=dest;

        while(rd<end){
            backend.ReadAuthData(cur,rd);
            rd=backend.getPos();
jan.koester's avatar
jan.koester committed
            if(strcmp(cur->uuid,plainid) == 0 && cur->type == type){
                memcpy(curec->Data,cur,sizeof(*cur));
jan.koester's avatar
jan.koester committed
                if(rd<backend.end()){
                    curec->_next=new AuthDataRecord;
                    curec=curec->_next;
                }
                found=true;
            }
        }
jan.koester's avatar
jan.koester committed

        delete cur;

jan.koester's avatar
jan.koester committed
        return found;
    }
};

jan.koester's avatar
jan.koester committed
int main(int argc, char *argv[]){
    cmdplus::CmdController &cmd=cmdplus::CmdController::getInstance();
jan.koester's avatar
jan.koester committed

jan.koester's avatar
jan.koester committed
    cmd.registerCmd("config",'c',true,nullptr,"Config Path");
jan.koester's avatar
jan.koester committed

jan.koester's avatar
jan.koester committed
    cmd.parseCmd(argc,argv);
jan.koester's avatar
jan.koester committed

jan.koester's avatar
jan.koester committed
    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"
                                );
jan.koester's avatar
jan.koester committed
    try {
jan.koester's avatar
jan.koester committed
        if(backend.end()<=sizeof(authdb::AuthHeader)){
jan.koester's avatar
jan.koester committed
            authdb::initalized=false;
jan.koester's avatar
jan.koester committed
        }

jan.koester's avatar
jan.koester committed
        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
        );
jan.koester's avatar
jan.koester committed

jan.koester's avatar
jan.koester committed
        authdb::AuthDB authdb(backend,httpd.getServerSocket());
jan.koester's avatar
jan.koester committed

jan.koester's avatar
jan.koester committed
        authdb.runEventloop();
jan.koester's avatar
jan.koester committed
    }catch(authdb::AuthBackendError &e){
        std::cerr << e.what() << std::endl;
    }
jan.koester's avatar
jan.koester committed
}