Added theweb nodes from rcpp_framework.

This commit is contained in:
Relintai 2022-06-30 13:23:14 +02:00
parent 48e8b85f7e
commit 121740f070
57 changed files with 4555 additions and 0 deletions

View File

@ -0,0 +1,12 @@
#!/usr/bin/env python
Import("env_mod")
Import("env")
env_mod.core_sources = []
env_mod.add_source_files(env_mod.core_sources, "*.cpp")
# Build it all as a library
lib = env_mod.add_library("admin_panel", env_mod.core_sources)
env.Prepend(LIBS=[lib])

View File

@ -0,0 +1,32 @@
#include "admin_node.h"
#include "web/html/form_validator.h"
#include "web/html/html_builder.h"
#include "web/http/cookie.h"
#include "web/http/http_session.h"
#include "web/http/request.h"
#include "web/http/session_manager.h"
void AdminNode::admin_handle_request_main(Request *request) {
}
String AdminNode::admin_get_section_name() {
return "";
}
void AdminNode::admin_add_section_links(Vector<AdminSectionLinkInfo> *links) {
}
bool AdminNode::admin_full_render() {
return false;
}
AdminNode::AdminNode() :
WebNode() {
}
AdminNode::~AdminNode() {
}

View File

@ -0,0 +1,40 @@
#ifndef ADMIN_NODE_H
#define ADMIN_NODE_H
#include "web/http/web_node.h"
#include "core/containers/vector.h"
#include "core/string.h"
class Request;
class FormValidator;
struct AdminSectionLinkInfo {
String name;
String link;
AdminSectionLinkInfo() {
}
AdminSectionLinkInfo(const String &p_name, const String &p_link) {
name = p_name;
link = p_link;
}
};
class AdminNode : public WebNode {
RCPP_OBJECT(AdminNode, WebNode);
public:
virtual void admin_handle_request_main(Request *request);
virtual String admin_get_section_name();
virtual void admin_add_section_links(Vector<AdminSectionLinkInfo> *links);
virtual bool admin_full_render();
AdminNode();
~AdminNode();
protected:
};
#endif

View File

@ -0,0 +1,171 @@
#include "admin_panel.h"
#include "web/html/form_validator.h"
#include "web/html/html_builder.h"
#include "web/http/cookie.h"
#include "web/http/http_session.h"
#include "web/http/request.h"
#include "web/http/session_manager.h"
#include "web/http/web_permission.h"
#include "admin_node.h"
void AdminPanel::handle_request_main(Request *request) {
if (_web_permission.is_valid()) {
if (_web_permission->activate(request)) {
return;
}
}
String seg = request->get_current_path_segment();
if (seg == "") {
render_admin_panel_list(request);
return;
}
for (int i = 0; i < _controllers.size(); ++i) {
AdminPanelSection &s = _controllers[i];
if (s.section_url == seg) {
AdminNode *c = s.controller;
request->push_path();
if (c->admin_full_render()) {
c->admin_handle_request_main(request);
return;
}
// render_menu(request);
render_headers(request);
render_segment_body_top(request);
render_controller_panel(request, c);
render_footer(request);
// request->pop_path();
return;
}
}
request->send_error(404);
}
void AdminPanel::render_admin_panel_list(Request *request) {
render_headers(request);
render_main_body_top(request);
String rootlink = request->get_url_root();
HTMLBuilder b;
Vector<AdminSectionLinkInfo> links;
b.div()->cls("content");
for (int i = 0; i < _controllers.size(); ++i) {
b.div()->cls("section");
AdminPanelSection &s = _controllers[i];
b.div()->cls("section_header");
b.w(s.controller->admin_get_section_name());
b.cdiv();
s.controller->admin_add_section_links(&links);
for (int j = 0; j < links.size(); ++j) {
AdminSectionLinkInfo &li = links[j];
b.div()->cls("section_entry");
b.a()->href(rootlink + s.section_url + "/" + li.link);
b.w(li.name);
b.ca();
b.cdiv();
}
links.clear();
b.cdiv();
}
b.cdiv();
render_footer(request);
request->body += b.result;
request->compile_and_send_body();
}
void AdminPanel::render_controller_panel(Request *request, AdminNode *controller) {
// set up headers
controller->admin_handle_request_main(request);
// set up footers
request->compile_and_send_body();
}
void AdminPanel::register_admin_controller(const String &section, AdminNode *controller) {
AdminPanelSection sec;
sec.section_url = section;
sec.controller = controller;
_controllers.push_back(sec);
}
void AdminPanel::clear() {
_controllers.clear();
}
void AdminPanel::render_headers(Request *request) {
request->head += _default_headers;
}
void AdminPanel::render_main_body_top(Request *request) {
request->body += _default_main_body_top;
}
void AdminPanel::render_segment_body_top(Request *request) {
request->body += _default_segment_body_top;
}
void AdminPanel::render_footer(Request *request) {
request->body += _default_footer;
}
void AdminPanel::set_default_header(const String &val) {
_default_headers = val;
}
void AdminPanel::set_default_main_body_top(const String &val) {
_default_main_body_top = val;
}
void AdminPanel::set_default_segment_body_top(const String &val) {
_default_segment_body_top = val;
}
void AdminPanel::set_default_footer(const String &val) {
_default_footer = val;
}
AdminPanel *AdminPanel::get_singleton() {
return _self;
}
AdminPanel::AdminPanel() :
WebNode() {
if (_self) {
printf("AdminPanel::AdminPanel(): Error! self is not null!/n");
}
_self = this;
}
AdminPanel::~AdminPanel() {
if (_self == this) {
_self = nullptr;
}
}
AdminPanel *AdminPanel::_self = nullptr;

View File

@ -0,0 +1,57 @@
#ifndef ADMIN_PANEL_H
#define ADMIN_PANEL_H
#include "web/http/web_node.h"
#include "core/string.h"
#include "core/containers/vector.h"
class Request;
class FormValidator;
class AdminNode;
class AdminPanel : public WebNode {
RCPP_OBJECT(AdminPanel, WebNode);
public:
void handle_request_main(Request *request);
virtual void render_admin_panel_list(Request *request);
virtual void render_controller_panel(Request *request, AdminNode *controller);
void register_admin_controller(const String &section, AdminNode *controller);
void clear();
virtual void render_headers(Request *request);
virtual void render_main_body_top(Request *request);
virtual void render_segment_body_top(Request *request);
virtual void render_footer(Request *request);
void set_default_header(const String &val);
void set_default_main_body_top(const String &val);
void set_default_segment_body_top(const String &val);
void set_default_footer(const String &val);
static AdminPanel *get_singleton();
AdminPanel();
~AdminPanel();
protected:
struct AdminPanelSection {
String section_url;
String name;
AdminNode *controller;
};
static AdminPanel *_self;
Vector<AdminPanelSection> _controllers;
String _default_headers;
String _default_main_body_top;
String _default_segment_body_top;
String _default_footer;
};
#endif

View File

@ -0,0 +1,26 @@
import os
import platform
import sys
def is_active():
return True
def get_name():
return "users"
def can_build():
return True
def get_opts():
return []
def get_flags():
return []
def configure(env):
pass

View File

@ -0,0 +1,12 @@
#!/usr/bin/env python
Import("env_mod")
Import("env")
env_mod.core_sources = []
env_mod.add_source_files(env_mod.core_sources, "*.cpp")
# Build it all as a library
lib = env_mod.add_library("paged_list", env_mod.core_sources)
env.Prepend(LIBS=[lib])

View File

@ -0,0 +1,139 @@
#include "browsable_folder_serve_node.h"
#include "core/os/directory.h"
#include "web/file_cache.h"
#include "web/html/html_builder.h"
#include "web/http/request.h"
void BrowsableFolderServeNode::_handle_request_main(Request *request) {
String file_name = request->get_path(true, false);
String *s = _folder_indexes[file_name];
if (!s) {
request->send_error(HTTP_STATUS_CODE_404_NOT_FOUND);
return;
}
if (should_render_menu) {
render_menu(request);
}
request->body += (*s);
request->compile_and_send_body();
}
void BrowsableFolderServeNode::render_index(Request *request) {
String *s = _folder_indexes["/"];
if (!s) {
return;
}
request->body += (*s);
}
void BrowsableFolderServeNode::render_preview(Request *request) {
}
void BrowsableFolderServeNode::load() {
if (serve_folder == "") {
return;
}
FolderServeNode::load();
evaluate_dir(serve_folder, true);
}
void BrowsableFolderServeNode::evaluate_dir(const String &path, const bool top_level) {
Ref<Directory> dir;
dir.instance();
ERR_FAIL_COND_MSG(dir->open_dir(path) != OK, "Error opening folde!r: " + String(path));
String dir_uri;
if (!top_level) {
dir_uri = path.substr(serve_folder.size(), path.size() - serve_folder.size());
} else {
dir_uri = "/";
}
Vector<String> folders;
Vector<String> files;
while (dir->next()) {
String np = dir->current_get_path();
np = np.substr(serve_folder.size(), np.size() - serve_folder.size());
if (dir->current_is_file()) {
files.push_back(np);
} else {
folders.push_back(np);
evaluate_dir(dir->current_get_path());
}
}
dir->close_dir();
folders.sort_inc();
files.sort_inc();
render_dir_page(dir_uri, folders, files, top_level);
}
void BrowsableFolderServeNode::render_dir_page(const String &dir_uri, const Vector<String> &folders, const Vector<String> &files, const bool top_level) {
HTMLBuilder b;
String uri = get_full_uri(false);
b.div("file_list");
{
if (!top_level) {
b.div("file_list_entry");
{
b.a(uri + dir_uri.path_get_prev_dir())->w("..")->ca();
}
b.cdiv();
}
for (int i = 0; i < folders.size(); ++i) {
b.div("file_list_entry");
{
b.a(uri + folders[i])->w("(Folder) ")->w(folders[i].path_get_basename())->ca();
}
b.cdiv();
}
for (int i = 0; i < files.size(); ++i) {
b.div("file_list_entry");
{
b.a(uri + files[i])->w("(File) ")->w(files[i].path_get_basename())->ca();
}
b.cdiv();
}
}
b.cdiv();
String *s = new String();
s->append_str(b.result);
_folder_indexes[dir_uri] = s;
}
BrowsableFolderServeNode::BrowsableFolderServeNode() :
FolderServeNode() {
should_render_menu = true;
}
BrowsableFolderServeNode::~BrowsableFolderServeNode() {
for (std::map<String, String *>::iterator E = _folder_indexes.begin(); E != _folder_indexes.end(); E++) {
if (E->second) {
delete E->second;
}
}
_folder_indexes.clear();
}

View File

@ -0,0 +1,44 @@
#ifndef BROWSABLE_FOLDER_SERVE_NODE_H
#define BROWSABLE_FOLDER_SERVE_NODE_H
#include "core/string.h"
#include <map>
#include "folder_serve_node.h"
// On top of serving the files from the folder set to it's serve_folder property,
// this class also generates HTML directory lists. (Similar to apache's directory listing)
// It caches folder contents on ENTER_TREE, it does not watch for folder changes yet.
// if (should_render_menu) -> render_menu(request)
// <div class="file_list">
// <div class="file_list_entry"><a href="/files/">..</a></div>
// <div class="file_list_entry"><a href="/files/test_folder/">(Folder) test_folder</a></div>
// <div class="file_list_entry"><a href="/files/test_file.md">(File) test_file.md</a></div>
// ...
// </div>
class BrowsableFolderServeNode : public FolderServeNode {
RCPP_OBJECT(BrowsableFolderServeNode, FolderServeNode);
public:
void _handle_request_main(Request *request);
void render_index(Request *request);
void render_preview(Request *request);
virtual void load();
void evaluate_dir(const String &path, const bool top_level = false);
virtual void render_dir_page(const String &dir_uri, const Vector<String> &folders, const Vector<String> &files, const bool top_level);
bool should_render_menu;
BrowsableFolderServeNode();
~BrowsableFolderServeNode();
protected:
std::map<String, String *> _folder_indexes;
};
#endif

View File

@ -0,0 +1,27 @@
import os
import platform
import sys
def is_active():
return True
def get_name():
return "paged_list"
def can_build():
return True
def get_opts():
return []
def get_flags():
return []
def configure(env):
pass

View File

@ -0,0 +1,67 @@
#include "folder_serve_node.h"
#include "web/http/request.h"
#include "web/http/web_permission.h"
void FolderServeNode::handle_request_main(Request *request) {
if (_web_permission.is_valid()) {
if (_web_permission->activate(request)) {
return;
}
}
const String &rp = request->get_current_path_segment();
if (rp == "") {
if (!try_route_request_to_children(request)) {
_handle_request_main(request);
}
return;
}
String file_name = request->get_path(true, false);
if (file_cache->wwwroot_has_file(file_name)) {
String fp = file_cache->wwwroot;
fp.append_path(file_name);
request->send_file(fp);
return;
}
if (!try_route_request_to_children(request)) {
_handle_request_main(request);
}
}
void FolderServeNode::load() {
file_cache->clear();
if (serve_folder == "") {
return;
}
serve_folder.path_clean_end_slash();
file_cache->wwwroot = serve_folder;
file_cache->wwwroot_refresh_cache();
}
void FolderServeNode::_notification(const int what) {
switch (what) {
case NOTIFICATION_ENTER_TREE:
load();
break;
default:
break;
}
}
FolderServeNode::FolderServeNode() :
WebNode() {
file_cache = new FileCache();
}
FolderServeNode::~FolderServeNode() {
}

View File

@ -0,0 +1,33 @@
#ifndef FOLDER_SERVE_NODE_H
#define FOLDER_SERVE_NODE_H
#include "core/string.h"
#include "web/file_cache.h"
#include "web/http/web_node.h"
// This class will serve the files from the folder set to it's serve_folder property.
// It will cache the folder's contents on ENTER_TREE, and will match against the cached list,
// this means directory walking (for example sending http://webapp.com/files/../../../etc/passwd),
// and other techniques like it should not be possible.
class FolderServeNode : public WebNode {
RCPP_OBJECT(FolderServeNode, WebNode);
public:
void handle_request_main(Request *request);
virtual void load();
void _notification(const int what);
String serve_folder;
FolderServeNode();
~FolderServeNode();
protected:
FileCache *file_cache;
};
#endif

View File

@ -0,0 +1,12 @@
#!/usr/bin/env python
Import("env_mod")
Import("env")
env_mod.core_sources = []
env_mod.add_source_files(env_mod.core_sources, "*.cpp")
# Build it all as a library
lib = env_mod.add_library("list_page", env_mod.core_sources)
env.Prepend(LIBS=[lib])

View File

@ -0,0 +1,27 @@
import os
import platform
import sys
def is_active():
return True
def get_name():
return "list_page"
def can_build():
return True
def get_opts():
return []
def get_flags():
return []
def configure(env):
pass

View File

@ -0,0 +1,197 @@
#include "list_page.h"
#include "core/math/math.h"
#include "web/html/html_builder.h"
#include "web/html/utils.h"
#include "web/http/web_permission.h"
#include <tinydir/tinydir.h>
#include <iostream>
void ListPage::handle_request_main(Request *request) {
if (_web_permission.is_valid()) {
if (_web_permission->activate(request)) {
return;
}
}
if (_pages.size() == 0) {
render_menu(request);
request->body += _no_entries_response;
request->compile_and_send_body();
return;
}
const String &cs = request->get_current_path_segment();
if (cs == "") {
render_menu(request);
render_index(request);
request->compile_and_send_body();
return;
}
if (!cs.is_uint()) {
request->send_error(HTTP_STATUS_CODE_404_NOT_FOUND);
return;
}
int p = cs.to_int();
p = ((p == 0) ? (0) : (p - 1));
if (p < 0 || p >= _pages.size()) {
request->send_error(HTTP_STATUS_CODE_404_NOT_FOUND);
return;
}
render_menu(request);
request->body += _pages[p];
request->compile_and_send_body();
}
void ListPage::render_index(Request *request) {
request->body += _pages[0];
}
void ListPage::render_preview(Request *request) {
}
void ListPage::load() {
if (folder == "") {
RLOG_ERR("Error: ListPage::load called, but a folder is not set!");
return;
}
Vector<String> files;
tinydir_dir dir;
if (tinydir_open(&dir, folder.c_str()) == -1) {
RLOG_ERR("Error opening ListPage::folder! folder: \n" + folder);
return;
}
while (dir.has_next) {
tinydir_file file;
if (tinydir_readfile(&dir, &file) == -1) {
tinydir_next(&dir);
continue;
}
if (!file.is_dir) {
String np = file.path;
files.push_back(np);
}
tinydir_next(&dir);
}
tinydir_close(&dir);
files.sort_inc();
Vector<String> list_entries;
for (uint32_t i = 0; i < files.size(); ++i) {
FILE *f = fopen(files[i].c_str(), "r");
if (!f) {
RLOG_ERR("Settings::parse_file: Error opening file!\n");
return;
}
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET); /* same as rewind(f); */
String fd;
fd.resize(fsize);
fread(&fd[0], 1, fsize, f);
fclose(f);
Utils::markdown_to_html(&fd);
list_entries.push_back(fd);
}
render_entries(list_entries);
render_no_entries_response();
}
void ListPage::render_entries(const Vector<String> &list_entries) {
if (list_entries.size() == 0) {
return;
}
int pages = Math::floorf_int(Math::divf(list_entries.size(), entry_per_page));
for (int i = 0; i < pages; ++i) {
String r = "";
int efrom = i * entry_per_page;
int eto = MIN((i + 1) * entry_per_page, list_entries.size());
r = render_page(i, pages, list_entries, efrom, eto);
_pages.push_back(r);
}
}
String ListPage::render_page(const int page_index, const int page_count, const Vector<String> &list_entries, const int efrom, const int eto) {
HTMLBuilder b;
b.div(main_div_class);
for (int i = efrom; i < eto; ++i) {
b.w(render_entry(list_entries[i]));
}
b.w(Utils::get_pagination(get_full_uri(), page_count, page_index, max_visible_navigation_links));
b.cdiv();
return b.result;
}
String ListPage::render_entry(const String &list_entry) {
HTMLBuilder b;
b.div(main_div_class);
b.div(empty_div_class)->w(list_entry)->cdiv();
b.cdiv();
return b.result;
}
void ListPage::render_no_entries_response() {
HTMLBuilder b;
b.div(empty_div_class)->w(placeholder_text)->cdiv();
_no_entries_response = b.result;
}
void ListPage::_notification(const int what) {
switch (what) {
case NOTIFICATION_ENTER_TREE:
load();
break;
case NOTIFICATION_EXIT_TREE:
_pages.clear();
break;
default:
break;
}
}
ListPage::ListPage() :
WebNode() {
max_visible_navigation_links = 6;
entry_per_page = 4;
main_div_class = "list_page";
entry_div_class = "list_entry";
empty_div_class = "list_entry_empty";
placeholder_text = "No content yet!";
}
ListPage::~ListPage() {
}

View File

@ -0,0 +1,66 @@
#ifndef LIST_PAGE_H
#define LIST_PAGE_H
#include "core/containers/vector.h"
#include "core/string.h"
#include "web/http/web_node.h"
#include "web/http/request.h"
// This class will load and generate pages from a folder of md files. It supports pagination,
// it will put entry_per_page md files per page. It generates html on enter tree, and caches everything.
// Each md file gets rendered into a div with a class of "list_entry"
// HTML (If there are entries):
// render_menu()
// <div class="list_page"> // Set the class via the main_div_class property
// <div class="list_entry">md file 1</div> // Set the class via the entry_div_class property
// <div class="list_entry">md file 2</div>
// ...
// <pagination>
// </div>
// HTML (If there are no entries):
// render_menu()
// <div class="list_page"> // Set the class via the main_div_class property
// <div class="list_entry_empty">No content yet!</div> // Set the class via the empty_div_class property, text via placeholder_text property
// </div>
class ListPage : public WebNode {
RCPP_OBJECT(ListPage, WebNode);
public:
void handle_request_main(Request *request);
void render_index(Request *request);
void render_preview(Request *request);
void load();
virtual void render_entries(const Vector<String> &list_entries);
virtual String render_page(const int page_index, const int page_count, const Vector<String> &list_entries, const int efrom, const int eto);
virtual String render_entry(const String &list_entry);
virtual void render_no_entries_response();
void _notification(const int what);
ListPage();
~ListPage();
bool paginate;
int max_visible_navigation_links;
int entry_per_page;
String folder;
String main_div_class;
String entry_div_class;
String empty_div_class;
String placeholder_text;
protected:
Vector<String> _pages;
String _no_entries_response;
};
#endif

View File

@ -0,0 +1,12 @@
#!/usr/bin/env python
Import("env_mod")
Import("env")
env_mod.core_sources = []
env_mod.add_source_files(env_mod.core_sources, "*.cpp")
# Build it all as a library
lib = env_mod.add_library("message_page", env_mod.core_sources)
env.Prepend(LIBS=[lib])

View File

@ -0,0 +1,27 @@
import os
import platform
import sys
def is_active():
return True
def get_name():
return "message_page"
def can_build():
return True
def get_opts():
return []
def get_flags():
return []
def configure(env):
pass

View File

@ -0,0 +1,85 @@
#include "message_page.h"
#include "database/database.h"
#include "database/query_builder.h"
#include "database/table_builder.h"
#include "database/query_result.h"
#include "web/http/web_permission.h"
void MessagePage::handle_request_main(Request *request) {
if (_web_permission.is_valid()) {
if (_web_permission->activate(request)) {
return;
}
}
Ref<QueryBuilder> b = db->get_query_builder();
b->select("text")->from("message_page")->end_command();
Ref<QueryResult> res = db->query(b->query_result);
Vector<String> msgs;
while (res->next_row()) {
msgs.push_back(res->get_cell(0));
}
String r = "<html><body>";
for (uint32_t i = 0; i < messages.size(); ++i) {
r += "<p>" + messages[i] + "</p><br>";
}
for (uint32_t i = 0; i < msgs.size(); ++i) {
r += "<p>" + msgs[i] + "</p><br>";
}
r += "</html></body>";
request->body += r;
request->compile_and_send_body();
}
void MessagePage::_migrate(const bool clear, const bool seed_db) {
Ref<TableBuilder> t = db->get_table_builder();
t->drop_table("message_page");
db->query_run(t->result);
printf("%s\n", t->result.c_str());
t->result.clear();
t->create_table("message_page")->integer("id")->auto_increment()->primary_key()->next_row()->varchar("text", 30)->ccreate_table();
printf("%s\n", t->result.c_str());
db->query(t->result);
Ref<QueryBuilder> b = db->get_query_builder();
b->insert("message_page")->values("default, 'aaewdwd'");
printf("%s\n", b->query_result.c_str());
db->query_run(b->query_result);
b->query_result.clear();
b->insert("message_page")->values("default, 'qqqqq'");
printf("%s\n", b->query_result.c_str());
db->query_run(b->query_result);
}
MessagePage::MessagePage() :
WebNode() {
messages.push_back("T message 1");
messages.push_back("T message 2");
}
MessagePage::~MessagePage() {
}

View File

@ -0,0 +1,26 @@
#ifndef MESSAGE_PAGE_H
#define MESSAGE_PAGE_H
#include "core/containers/vector.h"
#include "core/string.h"
#include "web/http/web_node.h"
#include "web/http/request.h"
class MessagePage : public WebNode {
RCPP_OBJECT(MessagePage, WebNode);
public:
void handle_request_main(Request *request);
void _migrate(const bool clear, const bool seed_db);
MessagePage();
~MessagePage();
Vector<String> messages;
};
#endif

View File

@ -0,0 +1,12 @@
#!/usr/bin/env python
Import("env_mod")
Import("env")
env_mod.core_sources = []
env_mod.add_source_files(env_mod.core_sources, "*.cpp")
# Build it all as a library
lib = env_mod.add_library("list_page", env_mod.core_sources)
env.Prepend(LIBS=[lib])

View File

@ -0,0 +1,27 @@
import os
import platform
import sys
def is_active():
return True
def get_name():
return "list_page"
def can_build():
return True
def get_opts():
return []
def get_flags():
return []
def configure(env):
pass

View File

@ -0,0 +1,173 @@
#include "paged_article.h"
#include "core/os/directory.h"
#include "web/html/utils.h"
#include "web/http/web_permission.h"
#include <iostream>
void PagedArticle::handle_request_main(Request *request) {
if (_web_permission.is_valid()) {
if (_web_permission->activate(request)) {
return;
}
}
const String &rp = request->get_current_path_segment();
if (request->get_remaining_segment_count() > 1 && rp == "files") {
String file_name = "/" + request->get_path_segment(request->get_current_segment_index() + 1);
if (file_cache->wwwroot_has_file(file_name)) {
String fp = file_cache->wwwroot + file_name;
request->send_file(fp);
return;
}
}
if (rp == "") {
render_menu(request);
render_index(request);
request->compile_and_send_body();
return;
}
const String *page = pages[rp];
if (page == nullptr) {
// bad url
request->send_error(404);
return;
}
render_menu(request);
request->body += (*page);
request->compile_and_send_body();
}
void PagedArticle::render_index(Request *request) {
// summary page
request->body += index_page;
}
void PagedArticle::render_preview(Request *request) {
}
void PagedArticle::load() {
ERR_FAIL_COND_MSG(articles_folder == "", "Error: PagedArticle::load called, but a articles_folder is not set!");
Ref<Directory> dir;
dir.instance();
ERR_FAIL_COND_MSG(dir->open_dir(articles_folder.c_str()) != OK, "Error opening PagedArticle::folder! folder: " + articles_folder);
Vector<String> files;
while (dir->next()) {
if (dir->current_is_file()) {
files.push_back(dir->current_get_name());
}
}
dir->close_dir();
if (files.size() == 0) {
return;
}
files.sort_inc();
for (uint32_t i = 0; i < files.size(); ++i) {
String file_path = articles_folder;
file_path.append_path(files[i]);
String fd;
ERR_CONTINUE_MSG(dir->read_file_into(file_path, &fd) != OK, "PagedArticle::load_folder: Error opening file! " + file_path);
Utils::markdown_to_html(&fd);
if (files[i] == "summary.md") {
summary = fd;
continue;
}
String pagination;
pagination = Utils::get_pagination_links(get_full_uri(), files, i);
String *finals = new String();
(*finals) += pagination;
(*finals) += fd;
(*finals) += pagination;
pages[files[i]] = finals;
if (i == 0) {
index_page = (*finals);
}
}
file_cache->clear();
if (serve_folder != "") {
if (serve_folder_relative) {
file_cache->wwwroot = articles_folder;
file_cache->wwwroot.append_path(serve_folder);
} else {
file_cache->wwwroot = serve_folder;
}
file_cache->wwwroot_refresh_cache();
}
if (summary == "") {
generate_summary();
}
}
String PagedArticle::get_index_page() {
return index_page;
}
String PagedArticle::get_summary() {
return summary;
}
void PagedArticle::generate_summary() {
summary = get_uri_segment();
}
void PagedArticle::_notification(const int what) {
switch (what) {
case NOTIFICATION_ENTER_TREE:
load();
break;
default:
break;
}
}
PagedArticle::PagedArticle() :
WebNode() {
file_cache = new FileCache();
serve_folder_relative = true;
serve_folder = "files";
}
PagedArticle::~PagedArticle() {
for (std::map<String, String *>::iterator it = pages.begin(); it != pages.end(); ++it) {
delete ((*it).second);
}
pages.clear();
delete file_cache;
}

View File

@ -0,0 +1,52 @@
#ifndef PAGED_ARTICLE_H
#define PAGED_ARTICLE_H
#include "core/containers/vector.h"
#include "core/string.h"
#include <map>
#include "web/file_cache.h"
#include "web/http/web_node.h"
#include "web/http/request.h"
// This class will load and process all md files from the folder set to it's articles_folder property,
// and serve every file from the directory set to it's serve_folder property.
// if it finds a summary.md, it will serve it as the root.
// It uses pagination.
// THe links is generates currently look like: <url>/01_test.md
// files are served under <url>/files/<file>
class PagedArticle : public WebNode {
RCPP_OBJECT(PagedArticle, WebNode);
public:
void handle_request_main(Request *request);
void render_index(Request *request);
void render_preview(Request *request);
void load();
void load_folder(const String &folder, const String &path);
String get_index_page();
String get_summary();
virtual void generate_summary();
void _notification(const int what);
PagedArticle();
~PagedArticle();
String articles_folder;
bool serve_folder_relative;
String serve_folder;
protected:
String index_page;
String summary;
std::map<String, String *> pages;
FileCache *file_cache;
};
#endif

View File

@ -0,0 +1,94 @@
#include "paged_articles.h"
#include "web/html/html_builder.h"
#include "core/os/directory.h"
#include "paged_article.h"
#include "web/html/utils.h"
#include <iostream>
void PagedArticles::_handle_request_main(Request *request) {
render_menu(request);
render_index(request);
request->compile_and_send_body();
}
void PagedArticles::render_index(Request *request) {
// summary page
request->body += index_page;
}
void PagedArticles::render_preview(Request *request) {
}
void PagedArticles::load() {
ERR_FAIL_COND_MSG(folder == "", "Error: PagedArticles::load called, but a folder is not set!");
if (folder.size() > 0 && folder[folder.size() - 1] == '/') {
folder.pop_back();
}
Ref<Directory> dir;
dir.instance();
ERR_FAIL_COND_MSG(dir->open_dir(folder) != OK, "Error opening PagedArticles::folder! folder: " + folder);
while (dir->next()) {
if (dir->current_is_dir()) {
String np = dir->current_get_path();
String fn = dir->current_get_name();
String ff = folder;
ff.append_path(fn);
PagedArticle *p = new PagedArticle();
String seg = dir->current_get_name();
p->articles_folder = ff;
p->set_uri_segment(seg);
add_child(p);
}
}
generate_index_page();
}
void PagedArticles::generate_index_page() {
HTMLBuilder b;
b.div("article_list");
for (int i = 0; i < get_child_count(); ++i) {
PagedArticle *a = Object::cast_to<PagedArticle>(get_child(i));
if (a) {
b.a(a->get_full_uri());
b.div("article_list_entry")->w(a->get_summary())->cdiv();
b.ca();
}
}
b.cdiv();
index_page = b.result;
}
void PagedArticles::_notification(const int what) {
switch (what) {
case NOTIFICATION_ENTER_TREE:
load();
break;
default:
break;
}
WebNode::_notification(what);
}
PagedArticles::PagedArticles() :
WebNode() {
}
PagedArticles::~PagedArticles() {
}

View File

@ -0,0 +1,50 @@
#ifndef PAGED_ARTICLES_H
#define PAGED_ARTICLES_H
#include "core/containers/vector.h"
#include "core/string.h"
#include "web/file_cache.h"
#include "web/http/web_node.h"
#include "web/http/request.h"
// This class will load and process all md files from the folder set to it's folder property,
// and generate one page from them. TThe files are processed in alphabetical order.
// The generated HTML looks like:
// render_menu()
// <div class="article_list_entry">
// Contents of the first md file.
// </div>
// <div class="article_list_entry">
// Contents of the second md file.
// </div>
// ...
// </div>
class PagedArticles : public WebNode {
RCPP_OBJECT(PagedArticles, WebNode);
public:
void _handle_request_main(Request *request);
void render_index(Request *request);
void render_preview(Request *request);
void load();
void generate_index_page();
void _notification(const int what);
PagedArticles();
~PagedArticles();
String folder;
protected:
String index_page;
};
#endif

View File

@ -0,0 +1,51 @@
#include "paged_articles_md_index.h"
#include "web/html/html_builder.h"
#include "web/http/request.h"
#include "web/http/web_permission.h"
void PagedArticlesMDIndex::handle_request_main(Request *request) {
if (_web_permission.is_valid()) {
if (_web_permission->activate(request)) {
return;
}
}
const String path = request->get_current_path_segment();
if (request->get_remaining_segment_count() == 0) {
main_page->handle_request_main(request);
return;
}
articles->handle_request_main(request);
}
void PagedArticlesMDIndex::render_index(Request *request) {
main_page->render_index(request);
}
void PagedArticlesMDIndex::render_preview(Request *request) {
main_page->render_preview(request);
}
void PagedArticlesMDIndex::load() {
main_page->folder = folder;
main_page->load();
articles->articles_folder = folder;
articles->serve_folder = folder + "/files";
articles->load();
}
PagedArticlesMDIndex::PagedArticlesMDIndex() :
WebNode() {
main_page = new ListPage();
articles = new PagedArticle();
}
PagedArticlesMDIndex::~PagedArticlesMDIndex() {
delete main_page;
delete articles;
}

View File

@ -0,0 +1,35 @@
#ifndef PAGED_ARTICLES_MD_INDEX_H
#define PAGED_ARTICLES_MD_INDEX_H
#include "core/string.h"
#include "web/http/web_node.h"
#include "web_modules/list_page/list_page.h"
#include "web_modules/paged_article/paged_article.h"
// Inherit from PagedArticles and override generate_index_page -> load and process md files in the set folder
// SHould probably be called something else. PagedArticlesMDIndex ?
class PagedArticlesMDIndex : public WebNode {
RCPP_OBJECT(PagedArticlesMDIndex, WebNode);
public:
void handle_request_main(Request *request);
void render_index(Request *request);
void render_preview(Request *request);
void load();
PagedArticlesMDIndex();
~PagedArticlesMDIndex();
String folder;
String base_path;
ListPage *main_page;
PagedArticle *articles;
};
#endif

View File

@ -0,0 +1,12 @@
#!/usr/bin/env python
Import("env_mod")
Import("env")
env_mod.core_sources = []
env_mod.add_source_files(env_mod.core_sources, "*.cpp")
# Build it all as a library
lib = env_mod.add_library("rbac", env_mod.core_sources)
env.Prepend(LIBS=[lib])

View File

@ -0,0 +1,31 @@
import os
import platform
import sys
def is_active():
return True
def get_name():
return "users"
def can_build():
return True
def get_opts():
return []
def get_flags():
return []
def configure(env):
pass
def get_module_dependencies():
return [
"admin_panel",
]

View File

@ -0,0 +1,872 @@
#include "rbac_controller.h"
#include "core/error_macros.h"
#include "web/html/form_validator.h"
#include "web/html/html_builder.h"
#include "web/http/cookie.h"
#include "web/http/http_session.h"
#include "web/http/request.h"
#include "web/http/session_manager.h"
#include "rbac_default_permissions.h"
#include "web_modules/users/user.h"
#include "database/database.h"
#include "database/database_manager.h"
#include "database/query_builder.h"
#include "database/query_result.h"
#include "database/table_builder.h"
void RBACController::handle_request_main(Request *request) {
}
void RBACController::create_validators() {
}
void RBACController::admin_handle_request_main(Request *request) {
String seg = request->get_current_path_segment();
if (seg == "") {
admin_render_rank_list(request);
return;
} else if (seg == "new_rank") {
request->push_path();
admin_handle_new_rank(request);
} else if (seg == "edit_rank") {
request->push_path();
admin_handle_edit_rank(request);
} else if (seg == "permission_editor") {
request->push_path();
admin_permission_editor(request);
}
}
void RBACController::admin_handle_new_rank(Request *request) {
if (request->get_method() == HTTP_METHOD_POST) {
Ref<RBACRank> rank;
rank.instance();
rank->name = request->get_parameter("name");
rank->name_internal = request->get_parameter("name_internal");
rank->settings = request->get_parameter("settings");
int base_permissions = 0;
for (int i = 0; i < _registered_permissions.size(); ++i) {
String param = request->get_parameter("perm_check_" + String::num(i));
if (param != "") {
base_permissions |= _registered_permissions[i].value;
}
}
rank->base_permissions = base_permissions;
int rank_permissions = 0;
for (int i = 0; i < _registered_rank_permissions.size(); ++i) {
String param = request->get_parameter("perm_rank_check_" + String::num(i));
if (param != "") {
rank_permissions |= _registered_rank_permissions[i].value;
}
}
rank->rank_permissions = rank_permissions;
db_save_rank(rank);
_ranks[rank->id] = rank;
request->send_redirect(request->get_url_root_parent() + "edit_rank/" + String::num(rank->id));
return;
}
RBACAdminRankViewData data;
render_rank_view(request, &data);
}
void RBACController::admin_handle_edit_rank(Request *request) {
String seg = request->get_current_path_segment();
//check whether it's numeric
//if (!seg.is)
int id = seg.to_int();
if (id == 0) {
RLOG_MSG("RBACController::admin_handle_edit_rank: id == 0!\n");
request->send_redirect(request->get_url_root_parent());
return;
}
Ref<RBACRank> rank = _ranks[id];
if (!rank.is_valid()) {
RLOG_MSG("RBACController::admin_handle_edit_rank: !rank.is_valid()\n");
request->send_redirect(request->get_url_root_parent());
return;
}
RBACAdminRankViewData data;
data.rank = rank;
if (request->get_method() == HTTP_METHOD_POST) {
rank->name = request->get_parameter("name");
rank->name_internal = request->get_parameter("name_internal");
rank->settings = request->get_parameter("settings");
int base_permissions = 0;
for (int i = 0; i < _registered_permissions.size(); ++i) {
String param = request->get_parameter("perm_check_" + String::num(i));
if (param != "") {
base_permissions |= _registered_permissions[i].value;
}
}
rank->base_permissions = base_permissions;
int rank_permissions = 0;
for (int i = 0; i < _registered_rank_permissions.size(); ++i) {
String param = request->get_parameter("perm_rank_check_" + String::num(i));
if (param != "") {
rank_permissions |= _registered_rank_permissions[i].value;
}
}
rank->rank_permissions = rank_permissions;
db_save_rank(rank);
data.messages.push_back("Save Success!");
}
render_rank_view(request, &data);
}
void RBACController::render_rank_view(Request *request, RBACAdminRankViewData *data) {
int id = 0;
String name = "";
String name_internal = "";
String settings = "";
int base_permissions = 0;
int rank_permissions = 0;
if (data->rank.is_valid()) {
id = data->rank->id;
name = data->rank->name;
name_internal = data->rank->name_internal;
settings = data->rank->settings;
base_permissions = data->rank->base_permissions;
rank_permissions = data->rank->rank_permissions;
}
HTMLBuilder b;
b.h4()->f()->a()->href(request->get_url_root_parent())->f()->w("<- Back")->ca()->ch4();
b.h4()->f()->w("RBAC Editor")->ch4();
b.div()->cls("messages");
for (int i = 0; i < data->messages.size(); ++i) {
b.w(data->messages[i])->br();
}
b.cdiv();
b.form()->method("POST")->action(request->get_url_root() + String::num(id));
{
b.csrf_token(request);
//b.input()->type("hidden")->name("id")->value(String::num(id))->f()->cinput();
b.w("Name:")->br();
b.input()->type("text")->name("name")->value(name)->f()->br();
b.w("Name (Internal):")->br();
b.input()->type("text")->name("name_internal")->value(name_internal)->f()->cinput()->br();
b.w("Custom Settings:")->br();
b.input()->type("text")->name("settings")->value(settings)->f()->cinput()->br();
b.w("Base Permissions:")->br();
for (int i = 0; i < _registered_permissions.size(); ++i) {
String checkbox_name = "perm_check_" + String::num(i);
b.input()->type("checkbox")->name(checkbox_name)->value(checkbox_name)->id(checkbox_name)->checked((base_permissions & _registered_permissions[i].value) != 0);
b.label()->fora(checkbox_name)->f()->w(_registered_permissions[i].name)->clabel();
}
b.br();
b.w("Rank Permissions:")->br();
for (int i = 0; i < _registered_rank_permissions.size(); ++i) {
String checkbox_name = "perm_rank_check_" + String::num(i);
b.input()->type("checkbox")->name(checkbox_name)->value(checkbox_name)->id(checkbox_name)->checked((rank_permissions & _registered_rank_permissions[i].value) != 0);
b.label()->fora(checkbox_name)->f()->w(_registered_rank_permissions[i].name)->clabel();
}
b.br();
b.input()->type("submit")->value("Save");
}
b.cform();
request->body += b.result;
}
void RBACController::admin_permission_editor(Request *request) {
String seg = request->get_current_path_segment();
//check whether it's numeric
//if (!seg.is)
int id = seg.to_int();
if (id == 0) {
RLOG_MSG("RBACController::admin_permission_editor: id == 0!\n");
request->send_redirect(request->get_url_root_parent());
return;
}
Ref<RBACRank> rank = _ranks[id];
if (!rank.is_valid()) {
RLOG_MSG("RBACController::admin_permission_editor: !rank.is_valid()\n");
request->send_redirect(request->get_url_root_parent());
return;
}
RBACAdminEditPermissionView data;
data.rank = rank;
request->push_path();
String segn = request->get_current_path_segment();
if (segn == "") {
admin_render_permission_editor_main_view(request, &data);
return;
}
if (segn == "new") {
request->push_path();
if (request->get_method() == HTTP_METHOD_POST) {
if (admin_process_permission_editor_entry_edit_create_post(request, &data)) {
return;
}
}
admin_render_permission_editor_entry_edit_create_view(request, &data);
return;
}
if (segn.is_uint()) {
int perm_index = segn.to_int();
request->push_path();
if (perm_index < 0 || perm_index >= rank->permissions.size()) {
RLOG_ERR("(perm_index < 0 || perm_index >= rank->permissions.size())!\n");
request->send_redirect(request->get_url_root_parent());
return;
}
data.permission = rank->permissions[perm_index];
if (!data.permission.is_valid()) {
RLOG_ERR("(!data.permission.is_valid()\n");
request->send_error(503);
return;
}
if (request->get_method() == HTTP_METHOD_POST) {
if (admin_process_permission_editor_entry_edit_create_post(request, &data)) {
return;
}
}
admin_render_permission_editor_entry_edit_create_view(request, &data);
return;
}
request->send_error(404);
}
void RBACController::admin_render_permission_editor_main_view(Request *request, RBACAdminEditPermissionView *data) {
HTMLBuilder b;
Ref<RBACRank> rank = data->rank;
b.h4()->f()->a()->href(request->get_url_root_parent(2))->f()->w("<- Back")->ca()->ch4();
b.h4()->f()->w("RBAC Editor")->ch4();
b.div()->cls("heading");
{
b.w("[ Id ]: ")->wn(rank->id)->w(", [ Name ]: ")->w(rank->name)->w(", [ Name Internal ]: ")->w(rank->name_internal);
}
b.cdiv();
b.br();
for (int i = 0; i < rank->permissions.size(); ++i) {
Ref<RBACPermission> perm = rank->permissions[i];
if (!perm.is_valid()) {
RLOG_ERR("RBACController::admin_render_permission_editor_main_view: !perm.is_valid()\n");
continue;
}
b.div()->cls("row");
{
b.a()->href(request->get_url_root() + String::num(i));
b.w("-- Rank: [ Id ]: ")->wn(perm->id)->w(", [ Rank Id ]: ")->wn(perm->rank_id)->w(", [ Name ]: ")->w(perm->name);
b.w(" [ URL ]: ")->w(perm->url)->w(", [ Sort Order ]: ")->wn(perm->sort_order);
b.w(" [ Permissions ]: ");
int pcount = 0;
int perms = perm->permissions;
for (int i = 0; i < _registered_permissions.size(); ++i) {
if ((_registered_permissions[i].value & perms) != 0) {
if (pcount > 0) {
b.w(", ");
}
b.w(_registered_permissions[i].name);
++pcount;
}
}
if (pcount == 0) {
b.w("- None -");
}
b.ca();
}
b.cdiv();
}
b.br();
b.a()->href(request->get_url_root("new"));
b.w("New Permission");
b.ca();
request->body += b.result;
}
void RBACController::admin_render_permission_editor_entry_edit_create_view(Request *request, RBACAdminEditPermissionView *data) {
HTMLBuilder b;
Ref<RBACRank> rank = data->rank;
Ref<RBACPermission> perm = data->permission;
String name;
String url;
int sort_order = 0;
int permissions = 0;
if (perm.is_valid()) {
name = perm->name;
url = perm->url;
sort_order = perm->sort_order;
permissions = perm->permissions;
}
b.h4()->f()->a()->href(request->get_url_root_parent())->f()->w("<- Back")->ca()->ch4();
b.h4()->f()->w("RBAC Editor")->ch4();
b.br();
b.div()->cls("messages");
for (int i = 0; i < data->messages.size(); ++i) {
b.w(data->messages[i])->br();
}
b.cdiv();
b.br();
b.div()->cls("heading");
{
b.w("Rank: [ Id ]: ")->wn(rank->id)->w(", [ Name ]: ")->w(rank->name)->w(", [ Name Internal ]: ")->w(rank->name_internal);
}
b.cdiv();
b.br();
b.form()->method("POST")->action(request->get_url_root());
{
b.csrf_token(request);
b.w("Name:")->br();
b.input()->type("text")->name("name")->value(name)->f()->br();
b.w("URL:")->br();
b.input()->type("text")->name("url")->value(url)->f()->cinput()->br();
b.w("Permissions:")->br();
for (int i = 0; i < _registered_permissions.size(); ++i) {
String checkbox_name = "perm_check_" + String::num(i);
b.input()->type("checkbox")->name(checkbox_name)->value(checkbox_name)->id(checkbox_name)->checked((permissions & _registered_permissions[i].value) != 0);
b.label()->fora(checkbox_name)->f()->w(_registered_permissions[i].name)->clabel();
}
b.br();
b.input()->type("submit")->value("Save");
}
b.cform();
request->body += b.result;
}
bool RBACController::admin_process_permission_editor_entry_edit_create_post(Request *request, RBACAdminEditPermissionView *data) {
Ref<RBACRank> rank = data->rank;
Ref<RBACPermission> perm = data->permission;
if (!perm.is_valid()) {
perm.instance();
perm->rank_id = rank->id;
if (rank->permissions.size() > 0) {
Ref<RBACPermission> p = rank->permissions[rank->permissions.size() - 1];
perm->sort_order = p->sort_order + 1;
}
rank->permissions.push_back(perm);
}
perm->name = request->get_parameter("name");
perm->url = request->get_parameter("url");
int permissions = 0;
for (int i = 0; i < _registered_permissions.size(); ++i) {
String param = request->get_parameter("perm_check_" + String::num(i));
if (param != "") {
permissions |= _registered_permissions[i].value;
}
}
perm->permissions = permissions;
//set this up in the form by default
//perm->sort_order = request->get_parameter("sort_order").to_int();
db_save_permission(perm);
if (perm->id == 0) {
RLOG_ERR("RBACController::admin_process_permission_editor_entry_edit_create_post: perm->id == 0!\n");
}
request->send_redirect(request->get_url_root_parent());
return true;
}
void RBACController::admin_render_rank_list(Request *request) {
HTMLBuilder b;
b.h4()->f()->a()->href(request->get_url_root_parent())->f()->w("<- Back")->ca()->ch4();
b.h4()->f()->w("RBAC Editor")->ch4();
for (std::map<int, Ref<RBACRank> >::iterator p = _ranks.begin(); p != _ranks.end(); p++) {
Ref<RBACRank> r = p->second;
if (!r.is_valid()) {
continue;
}
b.div()->cls("row");
{
b.a()->href(request->get_url_root("permission_editor/") + String::num(r->id));
b.w("[ Id ]: ")->wn(r->id)->w(", [ Name ]: ")->w(r->name)->w(", [ Name Internal ]: ")->w(r->name_internal);
b.w(", [ Base Permissions ]: ");
int pcount = 0;
int perms = r->base_permissions;
for (int i = 0; i < _registered_permissions.size(); ++i) {
if ((_registered_permissions[i].value & perms) != 0) {
if (pcount > 0) {
b.w(", ");
}
b.w(_registered_permissions[i].name);
++pcount;
}
}
if (pcount == 0) {
b.w("- None -");
}
b.w(", [ Rank Permissions ]: ");
pcount = 0;
perms = r->rank_permissions;
for (int i = 0; i < _registered_rank_permissions.size(); ++i) {
if ((_registered_rank_permissions[i].value & perms) != 0) {
if (pcount > 0) {
b.w(", ");
}
b.w(_registered_rank_permissions[i].name);
++pcount;
}
}
if (pcount == 0) {
b.w("- None -");
}
b.ca();
b.w(" - ");
b.a()->href(request->get_url_root("edit_rank/") + String::num(r->id));
b.w("[ Edit ]");
b.ca();
}
b.cdiv();
}
b.br();
b.a()->href(request->get_url_root("new_rank"));
b.w("New Rank");
b.ca();
request->body += b.result;
}
void RBACController::admin_render_rank_editor(Request *request) {
}
String RBACController::admin_get_section_name() {
return "Role Based Access Control";
}
void RBACController::admin_add_section_links(Vector<AdminSectionLinkInfo> *links) {
links->push_back(AdminSectionLinkInfo("Editor", ""));
}
void RBACController::register_permission(const String &name, const int val) {
_registered_permissions.push_back(PermissionEntry(name, val));
}
void RBACController::register_rank_permission(const String &name, const int val) {
_registered_rank_permissions.push_back(PermissionEntry(name, val));
}
void RBACController::clear_registered_permissions() {
_registered_permissions.clear();
_registered_rank_permissions.clear();
}
void RBACController::initialize() {
_ranks = db_load_ranks();
_default_rank_id = db_get_default_rank();
_default_user_rank_id = db_get_default_user_rank();
register_permissions();
}
void RBACController::register_permissions() {
register_permission("Create", User::PERMISSION_CREATE);
register_permission("Read", User::PERMISSION_READ);
register_permission("Update", User::PERMISSION_UPDATE);
register_permission("Delete", User::PERMISSION_DELETE);
register_rank_permission("Admin Panel", RBAC_RANK_PERMISSION_ADMIN_PANEL);
register_rank_permission("Use Redirect", RBAC_RANK_PERMISSION_USE_REDIRECT);
}
Ref<RBACRank> RBACController::get_rank(int rank_id) {
return _ranks[rank_id];
}
int RBACController::get_default_user_rank_id() {
return _default_user_rank_id;
}
Ref<RBACRank> RBACController::get_default_user_rank() {
return _ranks[get_default_user_rank_id()];
}
int RBACController::get_default_rank_id() {
return _default_rank_id;
}
Ref<RBACRank> RBACController::get_default_rank() {
return _ranks[get_default_rank_id()];
}
String &RBACController::get_redirect_url() {
return _redirect_url;
}
bool RBACController::continue_on_missing_default_rank() {
//todo, add setting
return false;
}
//DB
std::map<int, Ref<RBACRank> > RBACController::db_load_ranks() {
std::map<int, Ref<RBACRank> > ranks;
Ref<QueryBuilder> qb = get_query_builder();
qb->select("id,name,name_internal,settings,base_permissions,rank_permissions")->from(_rbac_ranks_table);
Ref<QueryResult> res = qb->run();
while (res->next_row()) {
Ref<RBACRank> r;
r.instance();
r->id = res->get_cell_int(0);
r->name = res->get_cell_str(1);
r->name_internal = res->get_cell_str(2);
r->settings = res->get_cell_str(3);
r->base_permissions = res->get_cell_int(4);
r->rank_permissions = res->get_cell_int(5);
ranks[r->id] = r;
}
qb->reset();
qb->select("id,rank_id,name,url,sort_order,permissions")->from(_rbac_permissions_table);
res = qb->run();
while (res->next_row()) {
Ref<RBACPermission> p;
p.instance();
p->id = res->get_cell_int(0);
p->rank_id = res->get_cell_int(1);
p->name = res->get_cell_str(2);
p->url = res->get_cell_str(3);
p->sort_order = res->get_cell_int(4);
p->permissions = res->get_cell_int(5);
Ref<RBACRank> r = ranks[p->rank_id];
if (!r.is_valid()) {
RLOG_ERR("RBACModel::load_permissions: !r.is_valid()!");
continue;
}
r->permissions.push_back(p);
}
for (std::map<int, Ref<RBACRank> >::iterator i = ranks.begin(); i != ranks.end(); ++i) {
Ref<RBACRank> r = i->second;
if (r.is_valid()) {
r->sort_permissions();
}
}
return ranks;
}
void RBACController::db_save(const Ref<RBACRank> &rank) {
db_save_rank(rank);
for (int i = 0; i < rank->permissions.size(); ++i) {
Ref<RBACPermission> permission = rank->permissions[i];
int rid = rank->id;
if (permission->rank_id != rid) {
permission->rank_id = rid;
}
db_save_permission(permission);
}
}
void RBACController::db_save_rank(const Ref<RBACRank> &rank) {
Ref<QueryBuilder> qb = get_query_builder();
if (rank->id == 0) {
qb->insert(_rbac_ranks_table, "name,name_internal,settings,base_permissions,rank_permissions")->values();
qb->val(rank->name)->val(rank->name_internal)->val(rank->settings)->val(rank->base_permissions)->val(rank->rank_permissions);
qb->cvalues();
qb->select_last_insert_id();
Ref<QueryResult> res = qb->run();
//qb->print();
Ref<RBACRank> r = rank;
r->id = res->get_last_insert_rowid();
} else {
qb->update(_rbac_ranks_table)->set();
qb->setp("name", rank->name);
qb->setp("name_internal", rank->name_internal);
qb->setp("settings", rank->settings);
qb->setp("base_permissions", rank->base_permissions);
qb->setp("rank_permissions", rank->rank_permissions);
qb->cset();
qb->where()->wp("id", rank->id);
qb->end_command();
qb->run_query();
//qb->print();
}
}
void RBACController::db_save_permission(const Ref<RBACPermission> &permission) {
Ref<QueryBuilder> qb = get_query_builder();
if (permission->id == 0) {
qb->insert(_rbac_permissions_table, "rank_id,name,url,sort_order,permissions")->values();
qb->val(permission->rank_id)->val(permission->name)->val(permission->url);
qb->val(permission->sort_order)->val(permission->permissions);
qb->cvalues();
qb->select_last_insert_id();
Ref<QueryResult> res = qb->run();
//qb->print();
Ref<RBACPermission> r = permission;
r->id = res->get_last_insert_rowid();
} else {
qb->update(_rbac_permissions_table)->set();
qb->setp("rank_id", permission->rank_id);
qb->setp("name", permission->name);
qb->setp("url", permission->url);
qb->setp("sort_order", permission->sort_order);
qb->setp("permissions", permission->permissions);
qb->cset();
qb->where()->wp("id", permission->id);
qb->end_command();
qb->run_query();
//qb->print();
}
}
int RBACController::db_get_default_rank() {
//todo, load this, and save it to a table (probably a new settings class)
return 3;
}
int RBACController::db_get_default_user_rank() {
//todo, load this, and save it to a table (probably a new settings class)
return 2;
}
String RBACController::db_get_redirect_url() {
//todo, load this, and save it to a table (probably a new settings class)
return String("/user/login");
}
void RBACController::create_table() {
Ref<TableBuilder> tb = get_table_builder();
tb->create_table(_rbac_ranks_table);
tb->integer("id")->auto_increment()->next_row();
tb->varchar("name", 60)->not_null()->next_row();
tb->varchar("name_internal", 100)->not_null()->next_row();
tb->varchar("settings", 200)->not_null()->next_row();
tb->integer("base_permissions")->not_null()->next_row();
tb->integer("rank_permissions")->not_null()->next_row();
tb->primary_key("id");
tb->ccreate_table();
//tb->run_query();
//tb->print();
//tb->result = "";
tb->create_table(_rbac_permissions_table);
tb->integer("id")->auto_increment()->next_row();
tb->integer("rank_id")->not_null()->next_row();
tb->varchar("name", 60)->not_null()->next_row();
tb->varchar("url", 100)->not_null()->next_row();
tb->integer("sort_order")->not_null()->next_row();
tb->integer("permissions")->not_null()->next_row();
tb->primary_key("id");
tb->foreign_key("rank_id")->references(_rbac_ranks_table, "id");
tb->ccreate_table();
tb->run_query();
//tb->print();
}
void RBACController::drop_table() {
Ref<TableBuilder> tb = get_table_builder();
tb->drop_table_if_exists(_rbac_permissions_table)->drop_table_if_exists(_rbac_ranks_table)->run_query();
//tb->print();
}
void RBACController::create_default_entries() {
Ref<RBACRank> admin;
admin.instance();
admin->name = "Admin";
admin->base_permissions = User::PERMISSION_ALL;
admin->rank_permissions = RBAC_RANK_PERMISSION_ADMIN_PANEL;
db_save_rank(admin);
Ref<RBACRank> user;
user.instance();
user->name = "User";
//user->base_permissions = User::PERMISSION_READ;
//user->rank_permissions = 0;
//temporary!
user->base_permissions = User::PERMISSION_ALL;
user->rank_permissions = RBAC_RANK_PERMISSION_ADMIN_PANEL;
db_save_rank(user);
Ref<RBACRank> guest;
guest.instance();
guest->name = "Guest";
guest->base_permissions = User::PERMISSION_READ;
guest->rank_permissions = RBAC_RANK_PERMISSION_USE_REDIRECT;
db_save_rank(guest);
}
RBACController *RBACController::get_singleton() {
return _self;
}
RBACController::RBACController() :
AdminNode() {
if (_self) {
printf("RBACController::RBACController(): Error! self is not null!/n");
}
_default_rank_id = 0;
_default_user_rank_id = 0;
_rbac_ranks_table = "rbac_ranks";
_rbac_permissions_table = "rbac_permissions";
_self = this;
}
RBACController::~RBACController() {
if (_self == this) {
_self = nullptr;
}
}
RBACController *RBACController::_self = nullptr;

View File

@ -0,0 +1,127 @@
#ifndef RBAC_CONTROLLER_H
#define RBAC_CONTROLLER_H
#include <map>
#include "web_modules/admin_panel/admin_node.h"
#include "core/containers/vector.h"
#include "core/string.h"
#include "rbac_permission.h"
#include "rbac_rank.h"
class Request;
class FormValidator;
class RBACController : public AdminNode {
RCPP_OBJECT(RBACController, AdminNode);
public:
void handle_request_main(Request *request);
void create_validators();
void admin_handle_request_main(Request *request);
String admin_get_section_name();
void admin_add_section_links(Vector<AdminSectionLinkInfo> *links);
struct RBACAdminRankViewData {
Ref<RBACRank> rank;
Vector<String> messages;
int id = 0;
String name = "";
String name_internal = "";
String settings = "";
int rank_permissions = 0;
};
void admin_handle_new_rank(Request *request);
void admin_handle_edit_rank(Request *request);
void render_rank_view(Request *request, RBACAdminRankViewData *data);
struct RBACAdminEditPermissionView {
Ref<RBACRank> rank;
Ref<RBACPermission> permission;
Vector<String> messages;
int rank_id = 0;
int permission_id = 0;
};
void admin_permission_editor(Request *request);
void admin_render_permission_editor_main_view(Request *request, RBACAdminEditPermissionView *data);
void admin_render_permission_editor_entry_edit_create_view(Request *request, RBACAdminEditPermissionView *data);
bool admin_process_permission_editor_entry_edit_create_post(Request *request, RBACAdminEditPermissionView *data);
void admin_render_rank_list(Request *request);
void admin_render_rank_editor(Request *request);
void register_permission(const String &name, const int val);
void register_rank_permission(const String &name, const int val);
void clear_registered_permissions();
void initialize();
virtual void register_permissions();
Ref<RBACRank> get_rank(int rank_id);
int get_default_user_rank_id();
Ref<RBACRank> get_default_user_rank();
int get_default_rank_id();
Ref<RBACRank> get_default_rank();
String &get_redirect_url();
bool continue_on_missing_default_rank();
// db
virtual std::map<int, Ref<RBACRank> > db_load_ranks();
virtual void db_save(const Ref<RBACRank> &rank);
virtual void db_save_rank(const Ref<RBACRank> &rank);
virtual void db_save_permission(const Ref<RBACPermission> &permission);
virtual int db_get_default_rank();
virtual int db_get_default_user_rank();
virtual String db_get_redirect_url();
void create_table();
void drop_table();
void create_default_entries();
static RBACController *get_singleton();
RBACController();
~RBACController();
protected:
static RBACController *_self;
int _default_rank_id;
int _default_user_rank_id;
std::map<int, Ref<RBACRank> > _ranks;
String _redirect_url;
struct PermissionEntry {
String name;
int value;
PermissionEntry() {
}
PermissionEntry(const String &p_name, const int p_val) {
name = p_name;
value = p_val;
}
};
String _rbac_ranks_table;
String _rbac_permissions_table;
Vector<PermissionEntry> _registered_permissions;
Vector<PermissionEntry> _registered_rank_permissions;
};
#endif

View File

@ -0,0 +1,9 @@
#ifndef RBAC_DEFAULT_PERMISSIONS_H
#define RBAC_DEFAULT_PERMISSIONS_H
enum RBACDefaultRankPermissions {
RBAC_RANK_PERMISSION_ADMIN_PANEL = 1 << 0,
RBAC_RANK_PERMISSION_USE_REDIRECT = 1 << 1,
};
#endif

View File

@ -0,0 +1,21 @@
#include "rbac_permission.h"
bool RBACPermission::is_smaller(const Ref<RBACPermission> &b) const {
if (!b.is_valid()) {
return true;
}
return sort_order < b->sort_order;
}
RBACPermission::RBACPermission() :
Resource() {
id = 0;
rank_id = 0;
sort_order = 0;
permissions = 0;
}
RBACPermission::~RBACPermission() {
}

View File

@ -0,0 +1,25 @@
#ifndef RBAC_PERMISSION_H
#define RBAC_PERMISSION_H
#include "core/string.h"
#include "core/resource.h"
class RBACPermission : public Resource {
RCPP_OBJECT(RBACPermission, Resource);
public:
int id;
int rank_id;
String name;
String url;
int sort_order;
int permissions;
bool is_smaller(const Ref<RBACPermission> &b) const;
RBACPermission();
~RBACPermission();
};
#endif

View File

@ -0,0 +1,77 @@
#include "rbac_rank.h"
#include "web/http/request.h"
Ref<RBACPermission> RBACRank::match_request(Request *request) {
const String &full_path = request->get_path_full();
Ref<RBACPermission> perm;
int current_max = 0;
for (int i = 0; i < permissions.size(); ++i) {
Ref<RBACPermission> p;
if (!p.is_valid()) {
continue;
}
int c = full_path.first_difference_index(p->url);
if (c > current_max) {
perm = p;
current_max = c;
}
}
return perm;
}
bool RBACRank::get_permissions(Request *request) {
int perm = base_permissions;
Ref<RBACPermission> match = match_request(request);
if (match.is_valid()) {
perm = match->permissions;
}
return perm;
}
bool RBACRank::has_permission(Request *request, const int permission) {
int perm = base_permissions;
Ref<RBACPermission> match = match_request(request);
if (match.is_valid()) {
perm = match->permissions;
}
return (perm & permission) != 0;
}
bool RBACRank::has_rank_permission(const int permission) {
return (rank_permissions & permission) != 0;
}
void RBACRank::sort_permissions() {
for (int i = 0; i < permissions.size(); ++i) {
for (int j = i + 1; j < permissions.size(); ++j) {
if (permissions[j]->is_smaller(permissions[i])) {
permissions.swap(i, j);
}
}
}
}
RBACRank::RBACRank() :
Resource() {
id = 0;
base_permissions = 0;
rank_permissions = 0;
}
RBACRank::~RBACRank() {
permissions.clear();
}

View File

@ -0,0 +1,40 @@
#ifndef RBAC_RANK_H
#define RBAC_RANK_H
#include "core/string.h"
#include "core/containers/vector.h"
#include "core/resource.h"
#include "rbac_permission.h"
class Request;
class RBACRank : public Resource {
RCPP_OBJECT(RBACRank, Resource);
public:
int id;
String name;
String name_internal;
String settings;
int base_permissions;
int rank_permissions;
Vector<Ref<RBACPermission> > permissions;
Ref<RBACPermission> match_request(Request *request);
bool get_permissions(Request *request);
bool has_permission(Request *request, const int permission);
bool has_rank_permission(const int permission);
void sort_permissions();
RBACRank();
~RBACRank();
};
#endif

View File

@ -0,0 +1,12 @@
#!/usr/bin/env python
Import("env_mod")
Import("env")
env_mod.core_sources = []
env_mod.add_source_files(env_mod.core_sources, "*.cpp")
# Build it all as a library
lib = env_mod.add_library("users", env_mod.core_sources)
env.Prepend(LIBS=[lib])

View File

@ -0,0 +1,33 @@
import os
import platform
import sys
def is_active():
return True
def get_name():
return "users"
def can_build():
return True
def get_opts():
return []
def get_flags():
return []
def configure(env):
pass
def get_module_dependencies():
return [
"users",
"rbac"
]

View File

@ -0,0 +1,37 @@
#include "rbac_user.h"
int RBACUser::get_permissions(Request *request) {
if (!rbac_rank.is_valid()) {
return 0;
}
return rbac_rank->get_permissions(request);
}
bool RBACUser::has_permission(Request *request, const int permission) {
if (!rbac_rank.is_valid()) {
return false;
}
return rbac_rank->has_permission(request, permission);
}
int RBACUser::get_additional_permissions(Request *request) {
if (!rbac_rank.is_valid()) {
return 0;
}
return rbac_rank->rank_permissions;
}
bool RBACUser::has_additional_permission(Request *request, const int permission) {
if (!rbac_rank.is_valid()) {
return false;
}
return rbac_rank->rank_permissions & permission;
}
RBACUser::RBACUser() :
User() {
}
RBACUser::~RBACUser() {
}

View File

@ -0,0 +1,27 @@
#ifndef RBAC_USER_H
#define RBAC_USER_H
#include "core/string.h"
#include "web_modules/users/user.h"
#include "web_modules/rbac/rbac_rank.h"
class Request;
class FormValidator;
class RBACUser : public User {
RCPP_OBJECT(RBACUser, User);
public:
Ref<RBACRank> rbac_rank;
int get_permissions(Request *request);
bool has_permission(Request *request, const int permission);
int get_additional_permissions(Request *request);
bool has_additional_permission(Request *request, const int permission);
RBACUser();
~RBACUser();
};
#endif

View File

@ -0,0 +1,146 @@
#include "rbac_user_controller.h"
#include "web/http/http_session.h"
#include "web/http/request.h"
#include "web_modules/rbac/rbac_controller.h"
#include "web_modules/rbac/rbac_default_permissions.h"
#include "rbac_user.h"
Ref<User> RBACUserController::db_get_user(const int id) {
Ref<RBACUser> u = UserController::db_get_user(id);
if (u.is_valid()) {
u->rbac_rank = RBACController::get_singleton()->get_rank(u->rank);
}
return u;
}
Ref<User> RBACUserController::db_get_user(const String &user_name_input) {
Ref<RBACUser> u = UserController::db_get_user(user_name_input);
if (u.is_valid()) {
u->rbac_rank = RBACController::get_singleton()->get_rank(u->rank);
}
return u;
}
Vector<Ref<User> > RBACUserController::db_get_all() {
Vector<Ref<User> > users = UserController::db_get_all();
for (int i = 0; i < users.size(); ++i) {
Ref<RBACUser> u = users[i];
if (u.is_valid()) {
u->rbac_rank = RBACController::get_singleton()->get_rank(u->rank);
}
}
return users;
}
Ref<User> RBACUserController::create_user() {
Ref<RBACUser> u;
u.instance();
u->rank = RBACController::get_singleton()->get_default_user_rank_id();
u->rbac_rank = RBACController::get_singleton()->get_rank(u->rank);
return u;
}
RBACUserController::RBACUserController() :
UserController() {
}
RBACUserController::~RBACUserController() {
}
// returnring true means handled, false means continue
bool RBACUserSessionSetupMiddleware::on_before_handle_request_main(Request *request) {
if (request->session.is_valid()) {
int user_id = request->session->get_int("user_id");
if (user_id != 0) {
Ref<RBACUser> u = UserController::get_singleton()->db_get_user(user_id);
if (u.is_valid()) {
request->reference_data["user"] = u;
} else {
// log
request->session->remove("user_id");
}
}
}
return false;
}
RBACUserSessionSetupMiddleware::RBACUserSessionSetupMiddleware() {
}
RBACUserSessionSetupMiddleware::~RBACUserSessionSetupMiddleware() {
}
// returnring true means handled, false means continue
bool RBACDefaultUserSessionSetupMiddleware::on_before_handle_request_main(Request *request) {
// note: add a new file handler middleware func, so basic file handling is easy to set up before this
Ref<RBACRank> rank;
if (request->session.is_valid()) {
int user_id = request->session->get_int("user_id");
if (user_id != 0) {
Ref<RBACUser> u = UserController::get_singleton()->db_get_user(user_id);
if (u.is_valid()) {
rank = u->rbac_rank;
request->reference_data["user"] = u;
} else {
// log
request->session->remove("user_id");
}
}
}
if (!rank.is_valid()) {
rank = RBACController::get_singleton()->get_default_rank();
if (!rank.is_valid()) {
if (RBACController::get_singleton()->continue_on_missing_default_rank()) {
RLOG_ERR("get_default_rank() has not been set up properly!!! Continuing!");
return false;
} else {
RLOG_ERR("get_default_rank() has not been set up properly!!! Sending 404!");
request->send_error(404);
return true;
}
}
}
if (!rank->has_permission(request, User::PERMISSION_READ)) {
if (rank->has_rank_permission(RBAC_RANK_PERMISSION_USE_REDIRECT)) {
// Note this can make the webapp prone to enumerations, if not done correctly
// e.g. redirect from /admin, but sending 404 on a non existing uri, which does not have
// a special rbac entry
request->send_redirect(RBACController::get_singleton()->get_redirect_url());
return true;
}
request->send_error(404);
return true;
}
return false;
}
RBACDefaultUserSessionSetupMiddleware::RBACDefaultUserSessionSetupMiddleware() {
}
RBACDefaultUserSessionSetupMiddleware::~RBACDefaultUserSessionSetupMiddleware() {
}

View File

@ -0,0 +1,54 @@
#ifndef RBAC_USER_CONTROLLER_H
#define RBAC_USER_CONTROLLER_H
#include "web_modules/users/user_controller.h"
#include "web/http/middleware.h"
class Request;
class RBACUserController : public UserController {
RCPP_OBJECT(RBACUserController, UserController);
public:
// db
Ref<User> db_get_user(const int id);
Ref<User> db_get_user(const String &user_name_input);
Vector<Ref<User> > db_get_all();
Ref<User> create_user();
RBACUserController();
~RBACUserController();
protected:
};
// just session setup
class RBACUserSessionSetupMiddleware : public Middleware {
RCPP_OBJECT(RBACUserSessionSetupMiddleware, Middleware);
public:
//returnring true means handled, false means continue
bool on_before_handle_request_main(Request *request);
RBACUserSessionSetupMiddleware();
~RBACUserSessionSetupMiddleware();
};
// this one also handles missing read permission / redirect
class RBACDefaultUserSessionSetupMiddleware : public Middleware {
RCPP_OBJECT(RBACDefaultUserSessionSetupMiddleware, Middleware);
public:
//returnring true means handled, false means continue
bool on_before_handle_request_main(Request *request);
RBACDefaultUserSessionSetupMiddleware();
~RBACDefaultUserSessionSetupMiddleware();
};
#endif

View File

@ -0,0 +1,12 @@
#!/usr/bin/env python
Import("env_mod")
Import("env")
env_mod.core_sources = []
env_mod.add_source_files(env_mod.core_sources, "*.cpp")
# Build it all as a library
lib = env_mod.add_library("paged_list", env_mod.core_sources)
env.Prepend(LIBS=[lib])

View File

@ -0,0 +1,27 @@
import os
import platform
import sys
def is_active():
return True
def get_name():
return "paged_list"
def can_build():
return True
def get_opts():
return []
def get_flags():
return []
def configure(env):
pass

View File

@ -0,0 +1,73 @@
#include "static_page.h"
#include "core/os/directory.h"
#include "web/file_cache.h"
#include "web/html/html_builder.h"
#include "web/html/utils.h"
#include "web/http/request.h"
void StaticPage::_handle_request_main(Request *request) {
if (should_render_menu) {
render_menu(request);
}
render_index(request);
request->compile_and_send_body();
}
void StaticPage::render_index(Request *request) {
request->body += data;
}
void StaticPage::render_preview(Request *request) {
request->body += preview_data;
}
void StaticPage::load_file(const String &path) {
Ref<Directory> d;
d.instance();
d->read_file_into(path, &data);
}
void StaticPage::load_and_process_file(const String &path) {
Ref<Directory> d;
d.instance();
d->read_file_into(path, &data);
if (path.file_get_extension() == "md") {
Utils::markdown_to_html(&data);
}
}
void StaticPage::load_md_file(const String &path) {
Ref<Directory> d;
d.instance();
d->read_file_into(path, &data);
Utils::markdown_to_html(&data);
}
void StaticPage::set_data_md(const String &d) {
data.clear();
Utils::markdown_to_html(&data);
}
void StaticPage::set_data(const String &d) {
data = d;
}
void StaticPage::set_preview_data(const String &d) {
preview_data = d;
}
StaticPage::StaticPage() :
WebNode() {
should_render_menu = true;
}
StaticPage::~StaticPage() {
}

View File

@ -0,0 +1,33 @@
#ifndef STATIC_PAGE_H
#define STATIC_PAGE_H
#include "core/string.h"
#include "web/http/web_node.h"
class StaticPage : public WebNode {
RCPP_OBJECT(StaticPage, WebNode);
public:
void _handle_request_main(Request *request);
void render_index(Request *request);
void render_preview(Request *request);
void load_file(const String &path);
void load_and_process_file(const String &path);
void load_md_file(const String &path);
void set_data_md(const String &d);
void set_data(const String &d);
void set_preview_data(const String &d);
String data;
String preview_data;
bool should_render_menu;
StaticPage();
~StaticPage();
};
#endif

View File

@ -0,0 +1,36 @@
#include "static_page_file.h"
#include "web/file_cache.h"
#include "web/html/html_builder.h"
#include "web/http/request.h"
void StaticPageFile::load() {
if (file_path == "") {
return;
}
if (process_if_can) {
load_and_process_file(file_path);
} else {
load_file(file_path);
}
}
void StaticPageFile::_notification(const int what) {
switch (what) {
case NOTIFICATION_ENTER_TREE:
load();
break;
default:
break;
}
}
StaticPageFile::StaticPageFile() :
StaticPage() {
process_if_can = true;
}
StaticPageFile::~StaticPageFile() {
}

View File

@ -0,0 +1,23 @@
#ifndef STATIC_PAGE_FILE_H
#define STATIC_PAGE_FILE_H
#include "core/string.h"
#include "static_page.h"
class StaticPageFile : public StaticPage {
RCPP_OBJECT(StaticPageFile, StaticPage);
public:
void load();
void _notification(const int what);
String file_path;
bool process_if_can;
StaticPageFile();
~StaticPageFile();
};
#endif

View File

@ -0,0 +1,57 @@
#include "static_page_folder_files.h"
#include "core/os/directory.h"
#include "web/file_cache.h"
#include "web/html/html_builder.h"
#include "web/html/utils.h"
#include "web/http/request.h"
void StaticPageFolderFiles::load() {
if (dir_path == "") {
return;
}
Ref<Directory> d;
d.instance();
ERR_FAIL_COND_MSG(!d->open_dir(dir_path), "Dir Path = " + dir_path);
String str;
while (d->has_next()) {
if (d->current_is_file()) {
String fn = dir_path;
fn.append_path(d->current_get_name_cstr());
d->read_file_into(fn, &str);
if (process_if_can && d->current_get_extension() == "md") {
Utils::markdown_to_html(&str);
}
append_data(str);
}
}
}
void StaticPageFolderFiles::append_data(const String &d) {
data += d;
}
void StaticPageFolderFiles::_notification(const int what) {
switch (what) {
case NOTIFICATION_ENTER_TREE:
load();
break;
default:
break;
}
}
StaticPageFolderFiles::StaticPageFolderFiles() :
StaticPage() {
process_if_can = true;
}
StaticPageFolderFiles::~StaticPageFolderFiles() {
}

View File

@ -0,0 +1,24 @@
#ifndef STATIC_PAGE_FOLDER_FILES_H
#define STATIC_PAGE_FOLDER_FILES_H
#include "core/string.h"
#include "static_page.h"
class StaticPageFolderFiles : public StaticPage {
RCPP_OBJECT(StaticPageFolderFiles, StaticPage);
public:
void load();
virtual void append_data(const String &d);
void _notification(const int what);
String dir_path;
bool process_if_can;
StaticPageFolderFiles();
~StaticPageFolderFiles();
};
#endif

View File

@ -0,0 +1,12 @@
#!/usr/bin/env python
Import("env_mod")
Import("env")
env_mod.core_sources = []
env_mod.add_source_files(env_mod.core_sources, "*.cpp")
# Build it all as a library
lib = env_mod.add_library("users", env_mod.core_sources)
env.Prepend(LIBS=[lib])

View File

@ -0,0 +1,27 @@
import os
import platform
import sys
def is_active():
return True
def get_name():
return "users"
def can_build():
return True
def get_opts():
return []
def get_flags():
return []
def configure(env):
pass

View File

@ -0,0 +1,107 @@
#include "user.h"
#include "rapidjson/document.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/rapidjson.h"
#include "rapidjson/stringbuffer.h"
#include <rapidjson/writer.h>
#include <tinydir/tinydir.h>
#include <cstdio>
#include "database/database_manager.h"
#include "database/query_builder.h"
#include "database/query_result.h"
#include "database/table_builder.h"
#include "web/html/form_validator.h"
#include "web/html/html_builder.h"
#include "web/http/cookie.h"
#include "web/http/http_session.h"
#include "web/http/request.h"
#include "web/http/session_manager.h"
#include "web/html/utils.h"
String User::to_json(rapidjson::Document *into) {
rapidjson::Document *document;
if (into) {
document = into;
} else {
document = new rapidjson::Document();
}
document->SetObject();
document->AddMember("id", id, document->GetAllocator());
document->AddMember("name", rapidjson::Value(name_user_input.c_str(), document->GetAllocator()), document->GetAllocator());
document->AddMember("email", rapidjson::Value(email_user_input.c_str(), document->GetAllocator()), document->GetAllocator());
document->AddMember("rank", rank, document->GetAllocator());
document->AddMember("pre_salt", rapidjson::Value(pre_salt.c_str(), document->GetAllocator()), document->GetAllocator());
document->AddMember("post_salt", rapidjson::Value(post_salt.c_str(), document->GetAllocator()), document->GetAllocator());
document->AddMember("password_hash", rapidjson::Value(password_hash.c_str(), document->GetAllocator()), document->GetAllocator());
document->AddMember("banned", banned, document->GetAllocator());
document->AddMember("password_reset_token", rapidjson::Value(password_reset_token.c_str(), document->GetAllocator()), document->GetAllocator());
document->AddMember("locked", locked, document->GetAllocator());
if (into) {
return "";
}
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
document->Accept(writer);
String s = buffer.GetString();
delete document;
return s;
}
void User::from_json(const String &p_data) {
rapidjson::Document data;
data.Parse(p_data.c_str());
rapidjson::Value uobj = data.GetObject();
id = uobj["id"].GetInt();
name_user_input = uobj["name"].GetString();
email_user_input = uobj["email"].GetString();
rank = uobj["rank"].GetInt();
pre_salt = uobj["pre_salt"].GetString();
post_salt = uobj["post_salt"].GetString();
password_hash = uobj["password_hash"].GetString();
banned = uobj["banned"].GetBool();
password_reset_token = uobj["password_reset_token"].GetString();
locked = uobj["locked"].GetBool();
}
int User::get_permissions(Request *request) {
return PERMISSION_ALL;
}
bool User::has_permission(Request *request, const int permission) {
return true;
}
int User::get_additional_permissions(Request *request) {
return 0;
}
bool User::has_additional_permission(Request *request, const int permission) {
return true;
}
User::User() :
Resource() {
rank = 0;
banned = false;
locked = false;
}
User::~User() {
}

View File

@ -0,0 +1,49 @@
#ifndef USER_H
#define USER_H
#include "core/string.h"
#include "core/resource.h"
#include "rapidjson/document.h"
#include <mutex>
class Request;
class FormValidator;
class User : public Resource {
RCPP_OBJECT(User, Resource);
public:
enum Permissions {
PERMISSION_CREATE = 1 << 0,
PERMISSION_READ = 1 << 1,
PERMISSION_UPDATE = 1 << 2,
PERMISSION_DELETE = 1 << 3,
PERMISSION_ALL = PERMISSION_CREATE | PERMISSION_READ | PERMISSION_UPDATE | PERMISSION_DELETE,
PERMISSION_NONE = 0
};
String name_user_input;
String email_user_input;
int rank;
String pre_salt;
String post_salt;
String password_hash;
bool banned;
String password_reset_token;
bool locked;
String to_json(rapidjson::Document *into = nullptr);
void from_json(const String &data);
virtual int get_permissions(Request *request);
virtual bool has_permission(Request *request, const int permission);
virtual int get_additional_permissions(Request *request);
virtual bool has_additional_permission(Request *request, const int permission);
User();
~User();
};
#endif

View File

@ -0,0 +1,828 @@
#include "user_controller.h"
#include "web/html/form_validator.h"
#include "web/html/html_builder.h"
#include "web/http/cookie.h"
#include "web/http/http_session.h"
#include "web/http/request.h"
#include "web/http/session_manager.h"
#include "web/http/web_permission.h"
#include "database/database.h"
#include "database/database_manager.h"
#include "database/query_builder.h"
#include "database/query_result.h"
#include "database/table_builder.h"
#include "crypto/hash/sha256.h"
void UserController::handle_request_main(Request *request) {
if (_web_permission.is_valid()) {
if (_web_permission->activate(request)) {
return;
}
}
if (request->session.is_valid()) {
Ref<User> u = request->reference_data["user"];
if (u.is_valid()) {
handle_request(u, request);
return;
}
}
const String &segment = request->get_current_path_segment();
if (segment == "") {
handle_login_request_default(request);
return;
} else if (segment == "login") {
handle_login_request_default(request);
return;
} else if (segment == "register") {
handle_register_request_default(request);
return;
}
handle_login_request_default(request);
}
void UserController::handle_login_request_default(Request *request) {
LoginRequestData data;
if (request->get_method() == HTTP_METHOD_POST) {
// this is probbaly not needed
// it's ok for now as I need to test the validators more
Vector<String> errors;
_login_validator->validate(request, &errors);
for (int i = 0; i < errors.size(); ++i) {
data.error_str += errors[i] + "<br>";
}
// not needed end
data.uname_val = request->get_parameter("username");
data.pass_val = request->get_parameter("password");
Ref<User> user = db_get_user(data.uname_val);
if (user.is_valid()) {
if (!check_password(user, data.pass_val)) {
data.error_str += "Invalid username or password!";
} else {
Ref<HTTPSession> session = request->get_or_create_session();
session->add("user_id", user->id);
SessionManager::get_singleton()->save_session(session);
::Cookie c = ::Cookie("session_id", session->session_id);
c.path = "/";
request->add_cookie(c);
render_login_success(request);
return;
}
} else {
data.error_str += "Invalid username or password!";
}
}
render_login_request_default(request, &data);
}
void UserController::render_login_request_default(Request *request, LoginRequestData *data) {
HTMLBuilder b;
b.w("Login");
b.br();
{
if (data->error_str.size() != 0) {
b.div()->cls("error");
b.w(data->error_str);
b.cdiv();
}
}
b.div()->cls("login");
{
// todo href path helper
b.form()->method("POST")->href("/user/login");
{
b.csrf_token(request);
b.w("Username");
b.br();
b.input()->type("text")->name("username")->value(data->uname_val);
b.cinput();
b.br();
b.w("Password");
b.br();
b.input()->type("password")->name("password");
b.cinput();
b.br();
b.input()->type("submit")->value("Send");
b.cinput();
}
b.cform();
}
b.cdiv();
request->body += b.result;
request->compile_and_send_body();
}
void UserController::handle_register_request_default(Request *request) {
RegisterRequestData data;
if (request->get_method() == HTTP_METHOD_POST) {
Vector<String> errors;
_registration_validator->validate(request, &errors);
for (int i = 0; i < errors.size(); ++i) {
data.error_str += errors[i] + "<br>";
}
data.uname_val = request->get_parameter("username");
data.email_val = request->get_parameter("email");
data.pass_val = request->get_parameter("password");
data.pass_check_val = request->get_parameter("password_check");
// todo username length etc check
// todo pw length etc check
if (is_username_taken(data.uname_val)) {
data.error_str += "Username already taken!<br>";
}
if (is_email_taken(data.email_val)) {
data.error_str += "Email already in use!<br>";
}
if (data.pass_val != data.pass_check_val) {
data.error_str += "The passwords did not match!<br>";
}
if (data.error_str.size() == 0) {
Ref<User> user;
user = create_user();
user->name_user_input = data.uname_val;
user->email_user_input = data.email_val;
create_password(user, data.pass_val);
db_save_user(user);
render_register_success(request);
return;
}
}
render_register_request_default(request, &data);
}
void UserController::render_register_success(Request *request) {
HTMLBuilder b;
b.div()->cls("success");
{
b.w("Registration successful! You can now log in!");
b.br();
b.a()->href("/user/login");
b.w(">> Login <<");
b.ca();
}
b.cdiv();
request->body += b.result;
request->compile_and_send_body();
}
void UserController::render_register_request_default(Request *request, RegisterRequestData *data) {
HTMLBuilder b;
b.w("Registration");
b.br();
{
if (data->error_str.size() != 0) {
b.div()->cls("error");
b.w(data->error_str);
b.cdiv();
}
}
b.div()->cls("register");
{
// todo href path helper
b.form()->method("POST")->href("/user/register");
{
b.csrf_token(request);
b.w("Username");
b.br();
b.input()->type("text")->name("username")->value(data->uname_val);
b.cinput();
b.br();
b.w("Email");
b.br();
b.input()->type("email")->name("email")->value(data->email_val);
b.cinput();
b.br();
b.w("Password");
b.br();
b.input()->type("password")->name("password");
b.cinput();
b.br();
b.w("Password again");
b.br();
b.input()->type("password")->name("password_check");
b.cinput();
b.br();
b.input()->type("submit")->value("Register");
b.cinput();
}
b.cform();
}
b.cdiv();
request->body += b.result;
request->compile_and_send_body();
}
void UserController::render_already_logged_in_error(Request *request) {
request->body += "You are already logged in.";
request->compile_and_send_body();
}
void UserController::render_login_success(Request *request) {
request->body = "Login Success!<br>";
// request->compile_and_send_body();
request->send_redirect("/user/settings");
}
void UserController::handle_request(Ref<User> &user, Request *request) {
const String &segment = request->get_current_path_segment();
if (segment == "") {
handle_main_page_request(user, request);
} else if (segment == "settings") {
handle_settings_request(user, request);
} else if (segment == "password_reset") {
handle_password_reset_request(user, request);
} else if (segment == "logout") {
handle_logout_request(user, request);
} else if (segment == "delete") {
handle_delete_request(user, request);
} else if (segment == "login") {
render_already_logged_in_error(request);
} else if (segment == "register") {
render_already_logged_in_error(request);
} else {
request->send_error(404);
}
}
void UserController::handle_main_page_request(Ref<User> &user, Request *request) {
request->body += "handle_main_page_request";
request->compile_and_send_body();
}
void UserController::handle_settings_request(Ref<User> &user, Request *request) {
SettingsRequestData data;
if (request->get_method() == HTTP_METHOD_POST) {
data.uname_val = request->get_parameter("username");
data.email_val = request->get_parameter("email");
data.pass_val = request->get_parameter("password");
data.pass_check_val = request->get_parameter("password_check");
bool changed = false;
Vector<String> errors;
bool valid = _profile_validator->validate(request, &errors);
for (int i = 0; i < errors.size(); ++i) {
data.error_str += errors[i] + "<br>";
}
if (valid) {
if (data.uname_val == user->name_user_input) {
data.uname_val = "";
}
if (data.email_val == user->email_user_input) {
data.email_val = "";
}
if (data.uname_val != "") {
if (is_username_taken(data.uname_val)) {
data.error_str += "Username already taken!<br>";
} else {
// todo sanitize for html special chars!
user->name_user_input = data.uname_val;
changed = true;
data.uname_val = "";
}
}
if (data.email_val != "") {
if (is_email_taken(data.email_val)) {
data.error_str += "Email already in use!<br>";
} else {
// todo sanitize for html special chars!
// also send email
user->email_user_input = data.email_val;
changed = true;
data.email_val = "";
}
}
if (data.pass_val != "") {
if (data.pass_val != data.pass_check_val) {
data.error_str += "The passwords did not match!<br>";
} else {
create_password(user, data.pass_val);
changed = true;
}
}
if (changed) {
db_save_user(user);
}
}
}
render_settings_request(user, request, &data);
}
void UserController::render_settings_request(Ref<User> &user, Request *request, SettingsRequestData *data) {
HTMLBuilder b;
b.w("Settings");
b.br();
{
if (data->error_str.size() != 0) {
b.div()->cls("error");
b.w(data->error_str);
b.cdiv();
}
}
b.div()->cls("settings");
{
// todo href path helper
b.form()->method("POST")->href("/user/settings");
{
b.csrf_token(request);
b.w("Username");
b.br();
b.input()->type("text")->name("username")->placeholder(user->name_user_input)->value(data->uname_val);
b.cinput();
b.br();
b.w("Email");
b.br();
b.input()->type("email")->name("email")->placeholder(user->email_user_input)->value(data->email_val);
b.cinput();
b.br();
b.w("Password");
b.br();
b.input()->type("password")->placeholder("*******")->name("password");
b.cinput();
b.br();
b.w("Password again");
b.br();
b.input()->type("password")->placeholder("*******")->name("password_check");
b.cinput();
b.br();
b.input()->type("submit")->value("Save");
b.cinput();
}
b.cform();
}
b.cdiv();
request->body += b.result;
request->compile_and_send_body();
}
void UserController::handle_password_reset_request(Ref<User> &user, Request *request) {
request->body += "handle_password_reset_request";
request->compile_and_send_body();
}
void UserController::handle_logout_request(Ref<User> &user, Request *request) {
request->remove_cookie("session_id");
db_save_user(user);
SessionManager::get_singleton()->delete_session(request->session->session_id);
request->session = nullptr;
HTMLBuilder b;
b.w("Logout successful!");
request->body += b.result;
request->compile_and_send_body();
}
void UserController::handle_delete_request(Ref<User> &user, Request *request) {
request->body += "handle_delete_request";
request->compile_and_send_body();
}
void UserController::create_validators() {
if (!_login_validator) {
// Login
_login_validator = new FormValidator();
_login_validator->new_field("username", "Username")->need_to_exist()->need_to_be_alpha_numeric()->need_minimum_length(5)->need_maximum_length(20);
FormField *pw = _login_validator->new_field("password", "Password");
pw->need_to_exist();
pw->need_to_have_lowercase_character()->need_to_have_uppercase_character();
pw->need_minimum_length(5);
}
if (!_registration_validator) {
// Registration
_registration_validator = new FormValidator();
_registration_validator->new_field("username", "Username")->need_to_exist()->need_to_be_alpha_numeric()->need_minimum_length(5)->need_maximum_length(20);
_registration_validator->new_field("email", "Email")->need_to_exist()->need_to_be_email();
FormField *pw = _registration_validator->new_field("password", "Password");
pw->need_to_exist();
pw->need_to_have_lowercase_character()->need_to_have_uppercase_character();
pw->need_minimum_length(5);
_registration_validator->new_field("password_check", "Password check")->need_to_match("password");
_registration_validator->new_field("email", "Email")->need_to_exist()->need_to_be_email();
}
if (!_profile_validator) {
_profile_validator = new FormValidator();
_profile_validator->new_field("username", "Username")->ignore_if_not_exists()->need_to_be_alpha_numeric()->need_minimum_length(5)->need_maximum_length(20);
_profile_validator->new_field("email", "Email")->ignore_if_not_exists()->need_to_be_email();
FormField *pw = _profile_validator->new_field("password", "Password");
pw->ignore_if_not_exists();
pw->need_to_have_lowercase_character()->need_to_have_uppercase_character();
pw->need_minimum_length(5);
_profile_validator->new_field("password_check", "Password check")->ignore_if_other_field_not_exists("password")->need_to_match("password");
}
}
Ref<User> UserController::db_get_user(const int id) {
if (id == 0) {
return Ref<User>();
}
Ref<QueryBuilder> b = get_query_builder();
b->select("username, email, rank, pre_salt, post_salt, password_hash, banned, password_reset_token, locked");
b->from(_table_name);
b->where()->wp("id", id);
b->end_command();
Ref<QueryResult> r = b->run();
if (!r->next_row()) {
return Ref<User>();
}
Ref<User> user;
user = create_user();
user->id = id;
user->name_user_input = r->get_cell(0);
user->email_user_input = r->get_cell(1);
user->rank = r->get_cell_int(2);
user->pre_salt = r->get_cell(3);
user->post_salt = r->get_cell(4);
user->password_hash = r->get_cell(5);
user->banned = r->get_cell_bool(6);
user->password_reset_token = r->get_cell(7);
user->locked = r->get_cell_bool(8);
return user;
}
Ref<User> UserController::db_get_user(const String &user_name_input) {
if (user_name_input == "") {
return Ref<User>();
}
Ref<QueryBuilder> b = get_query_builder();
b->select("id, email, rank, pre_salt, post_salt, password_hash, banned, password_reset_token, locked");
b->from(_table_name);
b->where()->wp("username", user_name_input);
b->end_command();
Ref<QueryResult> r = b->run();
if (!r->next_row()) {
return Ref<User>();
}
Ref<User> user;
user = create_user();
user->id = r->get_cell_int(0);
user->name_user_input = user_name_input;
user->email_user_input = r->get_cell(1);
user->rank = r->get_cell_int(2);
user->pre_salt = r->get_cell(3);
user->post_salt = r->get_cell(4);
user->password_hash = r->get_cell(5);
user->banned = r->get_cell_bool(6);
user->password_reset_token = r->get_cell(7);
user->locked = r->get_cell_bool(8);
return user;
}
void UserController::db_save_user(Ref<User> &user) {
Ref<QueryBuilder> b = get_query_builder();
if (user->id == 0) {
b->insert(_table_name, "username, email, rank, pre_salt, post_salt, password_hash, banned, password_reset_token, locked");
b->values();
b->val(user->name_user_input);
b->val(user->email_user_input);
b->val(user->rank);
b->val(user->pre_salt);
b->val(user->post_salt);
b->val(user->password_hash);
b->val(user->banned);
b->val(user->password_reset_token);
b->val(user->locked);
b->cvalues();
b->end_command();
b->select_last_insert_id();
Ref<QueryResult> r = b->run();
user->id = r->get_last_insert_rowid();
} else {
b->update(_table_name);
b->set();
b->setp("username", user->name_user_input);
b->setp("email", user->email_user_input);
b->setp("rank", user->rank);
b->setp("pre_salt", user->pre_salt);
b->setp("post_salt", user->post_salt);
b->setp("password_hash", user->password_hash);
b->setp("banned", user->banned);
b->setp("password_reset_token", user->password_reset_token);
b->setp("locked", user->locked);
b->cset();
b->where()->wp("id", user->id);
// b->print();
b->run_query();
}
}
Vector<Ref<User> > UserController::db_get_all() {
Ref<QueryBuilder> b = get_query_builder();
b->select("id, username, email, rank, pre_salt, post_salt, password_hash, banned, password_reset_token, locked");
b->from(_table_name);
b->end_command();
// b->print();
Vector<Ref<User> > users;
Ref<QueryResult> r = b->run();
while (r->next_row()) {
Ref<User> user = create_user();
user->id = r->get_cell_int(0);
user->name_user_input = r->get_cell(1);
user->email_user_input = r->get_cell(2);
user->rank = r->get_cell_int(3);
user->pre_salt = r->get_cell(4);
user->post_salt = r->get_cell(5);
user->password_hash = r->get_cell(6);
user->banned = r->get_cell_bool(7);
user->password_reset_token = r->get_cell(8);
user->locked = r->get_cell_bool(9);
users.push_back(user);
}
return users;
}
Ref<User> UserController::create_user() {
Ref<User> u;
u.instance();
return u;
}
bool UserController::is_username_taken(const String &user_name_input) {
Ref<QueryBuilder> b = get_query_builder();
b->select("id")->from(_table_name)->where("username")->like(user_name_input)->end_command();
Ref<QueryResult> r = b->run();
return r->next_row();
}
bool UserController::is_email_taken(const String &email_input) {
Ref<QueryBuilder> b = get_query_builder();
b->select("id")->from(_table_name)->where("username")->like(email_input)->end_command();
Ref<QueryResult> r = b->run();
return r->next_row();
}
bool UserController::check_password(const Ref<User> &user, const String &p_password) {
return hash_password(user, p_password) == user->password_hash;
}
void UserController::create_password(Ref<User> &user, const String &p_password) {
if (!user.is_valid()) {
printf("Error UserController::create_password !user.is_valid()!\n");
return;
}
// todo improve a bit
user->pre_salt = hash_password(user, user->name_user_input + user->email_user_input);
user->post_salt = hash_password(user, user->email_user_input + user->name_user_input);
user->password_hash = hash_password(user, p_password);
}
String UserController::hash_password(const Ref<User> &user, const String &p_password) {
if (!user.is_valid()) {
printf("Error UserController::hash_password !user.is_valid()!\n");
return "";
}
Ref<SHA256> s = SHA256::get();
String p = user->pre_salt + p_password + user->post_salt;
String c = s->compute(p);
return c;
}
void UserController::create_table() {
Ref<TableBuilder> tb = get_table_builder();
tb->create_table(_table_name);
tb->integer("id")->auto_increment()->next_row();
tb->varchar("username", 60)->not_null()->next_row();
tb->varchar("email", 100)->not_null()->next_row();
tb->integer("rank")->not_null()->next_row();
tb->varchar("pre_salt", 100)->next_row();
tb->varchar("post_salt", 100)->next_row();
tb->varchar("password_hash", 100)->next_row();
tb->integer("banned")->next_row();
tb->varchar("password_reset_token", 100)->next_row();
tb->integer("locked")->next_row();
tb->primary_key("id");
tb->ccreate_table();
tb->run_query();
// tb->print();
}
void UserController::drop_table() {
Ref<TableBuilder> tb = get_table_builder();
tb->drop_table_if_exists(_table_name)->run_query();
}
void UserController::create_default_entries() {
Ref<User> user;
user = create_user();
user->rank = 3;
user->name_user_input = "admin";
user->email_user_input = "admin@admin.com";
create_password(user, "Password");
db_save_user(user);
user = create_user();
user->rank = 1;
user->name_user_input = "user";
user->email_user_input = "user@user.com";
create_password(user, "Password");
db_save_user(user);
}
UserController *UserController::get_singleton() {
return _self;
}
UserController::UserController() :
WebNode() {
if (_self) {
printf("UserController::UserController(): Error! self is not null!/n");
}
_self = this;
create_validators();
}
UserController::~UserController() {
if (_self == this) {
_self = nullptr;
}
}
UserController *UserController::_self = nullptr;
FormValidator *UserController::_login_validator = nullptr;
FormValidator *UserController::_registration_validator = nullptr;
FormValidator *UserController::_profile_validator = nullptr;
String UserController::_path = "./";
String UserController::_table_name = "users";
// returnring true means handled, false means continue
bool UserSessionSetupMiddleware::on_before_handle_request_main(Request *request) {
if (request->session.is_valid()) {
int user_id = request->session->get_int("user_id");
if (user_id != 0) {
Ref<User> u = UserController::get_singleton()->db_get_user(user_id);
if (u.is_valid()) {
request->reference_data["user"] = u;
} else {
// log
request->session->remove("user_id");
}
}
}
return false;
}
UserSessionSetupMiddleware::UserSessionSetupMiddleware() {
}
UserSessionSetupMiddleware::~UserSessionSetupMiddleware() {
}

View File

@ -0,0 +1,118 @@
#ifndef USER_CONTROLLER_H
#define USER_CONTROLLER_H
#include "core/containers/vector.h"
#include "core/string.h"
#include "web/http/web_node.h"
#include "user.h"
#include "web/http/middleware.h"
class Request;
class FormValidator;
class UserController : public WebNode {
RCPP_OBJECT(UserController, WebNode);
public:
void handle_request_main(Request *request);
struct LoginRequestData {
String error_str;
String uname_val;
String pass_val;
};
virtual void handle_login_request_default(Request *request);
virtual void render_login_request_default(Request *request, LoginRequestData *data);
struct RegisterRequestData {
String error_str;
String uname_val;
String email_val;
String pass_val;
String pass_check_val;
};
virtual void handle_register_request_default(Request *request);
virtual void render_register_request_default(Request *request, RegisterRequestData *data);
virtual void render_register_success(Request *request);
virtual void render_already_logged_in_error(Request *request);
virtual void render_login_success(Request *request);
virtual void handle_request(Ref<User> &user, Request *request);
virtual void handle_main_page_request(Ref<User> &user, Request *request);
struct SettingsRequestData {
String error_str;
String uname_val;
String email_val;
String pass_val;
String pass_check_val;
};
virtual void handle_settings_request(Ref<User> &user, Request *request);
virtual void render_settings_request(Ref<User> &user, Request *request, SettingsRequestData *data);
virtual void handle_password_reset_request(Ref<User> &user, Request *request);
virtual void handle_logout_request(Ref<User> &user, Request *request);
virtual void handle_delete_request(Ref<User> &user, Request *request);
virtual void create_validators();
// db
virtual Ref<User> db_get_user(const int id);
virtual Ref<User> db_get_user(const String &user_name_input);
virtual void db_save_user(Ref<User> &user);
virtual Vector<Ref<User> > db_get_all();
virtual Ref<User> create_user();
bool is_username_taken(const String &user_name_input);
bool is_email_taken(const String &email_input);
virtual bool check_password(const Ref<User> &user, const String &p_password);
virtual void create_password(Ref<User> &user, const String &p_password);
virtual String hash_password(const Ref<User> &user, const String &p_password);
virtual void create_table();
virtual void drop_table();
virtual void create_default_entries();
static UserController *get_singleton();
UserController();
~UserController();
protected:
static UserController *_self;
static FormValidator *_login_validator;
static FormValidator *_registration_validator;
static FormValidator *_profile_validator;
String _file_path;
static String _path;
static String _table_name;
};
// just session setup
class UserSessionSetupMiddleware : public Middleware {
RCPP_OBJECT(UserSessionSetupMiddleware, Middleware);
public:
//returnring true means handled, false means continue
bool on_before_handle_request_main(Request *request);
UserSessionSetupMiddleware();
~UserSessionSetupMiddleware();
};
#endif