Skip to content

Commit

Permalink
Lot of work on long lived 'logged in user' sessions
Browse files Browse the repository at this point in the history
  • Loading branch information
matiu2 committed Jun 26, 2011
1 parent fcb987c commit 3a58adc
Show file tree
Hide file tree
Showing 17 changed files with 588 additions and 208 deletions.
66 changes: 8 additions & 58 deletions App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
#include <Wt/WString>
#include <Wt/WServer>
#include <Wt/WLogger>
#include <Wt/WDialog>
#include <Wt/WPushButton>
#include <Wt/WFileResource>
#include <string>
#include "LoginWindow.hpp"
#include "SessionStore.hpp"

namespace Wt {
class WEnvironment;
Expand All @@ -34,14 +34,14 @@ namespace Wt {
using Wt::WString;
using Wt::WApplication;
using Wt::WEnvironment;
using Wt::WDialog;
using Wt::WServer;
using mongo::ScopedDbConnection;

namespace vidanueva {

VidaApp::VidaApp(const WEnvironment &environment) : WApplication(environment), _pages(this) {
log("NOTICE") << "Resources URL: " << resourcesUrl();
_userSession = new SessionHandle(this);
// Set up our signals
_userChanged = new AppSignal(this);
// Add some styles
Expand All @@ -53,14 +53,14 @@ VidaApp::VidaApp(const WEnvironment &environment) : WApplication(environment), _
readConfigurationProperty("mongo-host", _mongoHostName);
readConfigurationProperty("mongo-db", _mongoDB);
readConfigurationProperty("mongo-users-table", mongoUsersTable);
_userManager.configure(_mongoHostName, _mongoDB, mongoUsersTable);
_userSession->configureMongo(_mongoHostName, _mongoDB, mongoUsersTable);
// Load our message bundles
// Bundles we'll use a lot
messageResourceBundle().use(appRoot() + "messages/MainWindow");
messageResourceBundle().use(appRoot() + "messages/DialogButtonBar");
messageResourceBundle().use(appRoot() + "messages/misc");
messageResourceBundle().use(appRoot() + "messages/Page");
messageResourceBundle().use(appRoot() + "messages/EditButtonBar");
messageResourceBundle().use(appRoot() + "messages/ButtonBar");
// Bundles we won't use quite so much
messageResourceBundle().use(appRoot() + "messages/LoginWindow", false);
messageResourceBundle().use(appRoot() + "messages/ControlPanel", false);
Expand All @@ -69,61 +69,10 @@ VidaApp::VidaApp(const WEnvironment &environment) : WApplication(environment), _
setTitle(WString::tr("main-title"));
_mainWindow = new MainWindow(root());
setBodyClass("yui-skin-sam");
// Hook up the url event handlers
internalPathChanged().connect(this, &VidaApp::onURLChange);
// Show the stuff from the right thing for the right path the entered on
setInternalPath(internalPath(), true);
}

/**
* @brief Called when the url is changed
*
* At the moment we're only interested in showing the login dialog if the user is logged in
*/
void VidaApp::onURLChange(const std::string& path) {
log("NOTICE") << "Changing path: " << path;
if (internalPathMatches("/login")) {
log("NOTICE") << "Matches login path";
if (_username == "") {
// If no-one's logged in. Show the login dialog
showLoginDialog();
} else {
log("NOTICE") << "GOING HOME 1";
goHome(); // Back to the home page .. can't login twice
}
} else if (internalPathMatches("/page/")) {
// Look up the page that belongs here
std::string pageName = internalPathNextPart("/page/");
Page* page = pages().load(pageName);
mainWindow()->setBody(page);
}
}

/**
* @brief Shows a modal login dialog
*/
void VidaApp::showLoginDialog() {
WDialog dialog(WString::tr("login"));
LoginWindow* loginWindow = new LoginWindow(dialog);
loginWindow->setFocus();
if (dialog.exec() == WDialog::Accepted) {
dialog.hide();
// See if we can log them in
_userManager.savePassword(loginWindow->username(), loginWindow->password());
if (_userManager.checkLogin(loginWindow->username(), loginWindow->password())) {
_username = loginWindow->username();
log("SECURITY") << loginWindow->username() << " logged in";
goHome(); // Back to the home page
_userChanged->emit(this);
return;
} else {
mainWindow()->setStatusText(WString::tr("Wrong username or password"));
log("SECURITY") << loginWindow->username() << " failed log in";
}
}
_username = ""; // If we make it here .. we're not logged in anymore
goHome(); // Back to the home page
}

/**
* @brief Insert/Update a record in the mongo DB.
*
Expand Down Expand Up @@ -169,8 +118,9 @@ int main(int argc, char **argv) {
WServer server(argv[0]);
server.setServerConfiguration(argc, argv, WTHTTP_CONFIGURATION);

server.addEntryPoint(Wt::Application, vidanueva::createApplication, "/vida", "/css/favicon.ico");
server.addEntryPoint(Wt::Application, vidanueva::createRedirectApp, "/", "/css/favicon.ico");
server.addEntryPoint(Wt::Application, vidanueva::createApplication, "", "/css/favicon.ico");
//server.addEntryPoint(Wt::Application, vidanueva::createApplication, "/", "/css/favicon.ico");
//server.addEntryPoint(Wt::Application, vidanueva::createRedirectApp, "/", "/css/favicon.ico");

if (server.start()) {
WServer::waitForShutdown();
Expand Down
10 changes: 3 additions & 7 deletions App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
#ifndef APP_HPP
#define APP_HPP

#include "UserManager.hpp"
#include "MainWindow.hpp"
#include <Wt/WApplication>
#include <Wt/WSignal>
#include <string>
#include <mongo/client/connpool.h>
#include "Page.hpp"
#include "SessionHandle.hpp"

namespace Wt {
class WEnvironment;
Expand All @@ -44,21 +44,17 @@ class VidaApp : public WApplication {
typedef Signal<VidaApp*> AppSignal;
private:
MainWindow* _mainWindow;
std::string _username; // Name of currently logged in user. "" if none
UserManager _userManager;
void onURLChange(const std::string& path);
void showLoginDialog();
AppSignal* _userChanged;
string _mongoHostName;
string _mongoDB;
PageFactory _pages;
SessionHandle* _userSession;
public:
VidaApp(const WEnvironment &environment);
bool loggedIn() { return !_username.empty(); } /// Returns true if a user is logged in, otherwise false if current user is anonymous
std::string username() { return _username; } /// Returns the username if someone is logged in, "" otherwise
AppSignal* userChanged() { return _userChanged; } /// An event triggered when a user logs in or logs out
MainWindow* mainWindow() { return _mainWindow; } /// A pointer to the main window widget
void goHome() { setInternalPath("/", true); }
SessionHandle* userSession() { return _userSession; } /// Returns a pointer to our session handle
const string& mongoHostname() { return _mongoHostName; } /// Returns the hostname of our mongo db
const string& mongoDB() { return _mongoDB; } /// Returns the name of the actual database inside of mongo
const string mongoNSFor(const string& tableName) { return _mongoDB + "." + tableName; } /// Returns the mongo namespace for any given tablename eg: "pages" => "vidanueva.pages"
Expand Down
7 changes: 5 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ CMAKE_MINIMUM_REQUIRED( VERSION 2.8 )

PROJECT(vidanueva)

ADD_DEFINITIONS(-Wall -Wextra)

# Copy resources needed
FILE(COPY debug-run.sh messages static-content DESTINATION .)
FILE(COPY debug-run.sh run.sh messages static-content DESTINATION .)

SET(DEBUG true)

Expand Down Expand Up @@ -36,7 +38,8 @@ include_directories(BEFORE ${Boost_INCLUDE_DIRS})

# Build the executable
ADD_EXECUTABLE(${PROJECT_NAME} App.cpp
MainWindow.cpp LoginWindow.cpp UserManager.cpp ControlPanel.cpp Page.cpp PageEdit.cpp)
MainWindow.cpp LoginWindow.cpp UserManager.cpp ControlPanel.cpp Page.cpp PageEdit.cpp
UserManager.cpp SessionStore.cpp SessionHandle.cpp)
TARGET_LINK_LIBRARIES(${PROJECT_NAME}
${Boost_SYSTEM_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SIGNALS_LIBRARY}
${Boost_DATE_TIME_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_REGEX_LIBRARY}
Expand Down
46 changes: 39 additions & 7 deletions LoginWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@

#include "LoginWindow.hpp"

#include <Wt/WRandom>
#include <Wt/WString>
#include <Wt/WLabel>
#include <Wt/WLineEdit>
#include <Wt/WLogger>
#include <Wt/WPushButton>
#include <Wt/WDialog>
#include <Wt/WContainerWidget>
#include "App.hpp"
#include "SessionStore.hpp"

using Wt::WString;

namespace vidanueva {

LoginWindow::LoginWindow(WDialog& parentDialog) : MoreAwesomeTemplate(parentDialog.contents()) {
LoginWindow::LoginWindow(WContainerWidget* parent) : MoreAwesomeTemplate(parent) {
setTemplateText(tr("login-template"));
addStyleClass("yui-gd dialog form"); // 1/3 + 2/3 style
bindAndCreateField(_usernameLabel, _usernameEdit, "username");
Expand All @@ -38,15 +42,43 @@ LoginWindow::LoginWindow(WDialog& parentDialog) : MoreAwesomeTemplate(parentDial
_passwordEdit->setEchoMode(WLineEdit::Password);

// Buttons
bindWidget("btn-bar", _btnBar = new DialogButtonBar(parentDialog));
bindWidget("btn-bar", _btnBar = new ButtonBar(tr("Login"), tr("Cancel")));
_btnBar->btn1()->clicked().connect(this, &LoginWindow::handleOKHit);
_btnBar->btn2()->clicked().connect(this, &LoginWindow::handleCancelHit);

// Hook up accept and reject signals
// All these do an accept
_passwordEdit->enterPressed().connect(&parentDialog, &WDialog::accept);
_passwordEdit->enterPressed().connect(this, &LoginWindow::handleOKHit);

// These do reject
_usernameEdit->escapePressed().connect(&parentDialog, &WDialog::accept);
_passwordEdit->escapePressed().connect(&parentDialog, &WDialog::accept);
_usernameEdit->escapePressed().connect(this, &LoginWindow::handleCancelHit);
_passwordEdit->escapePressed().connect(this, &LoginWindow::handleCancelHit);
}

/**
* @brief Called when the user hits OK to login
*/
void LoginWindow::handleOKHit() {
VidaApp* app = getApp();
// See if we can log them in
string username = _usernameEdit->text().toUTF8();
string password = _passwordEdit->text().toUTF8();
if (app->userSession()->tryLogin(username, password)) {
// Let the application know
app->log("SECURITY") << username << " logged in";
app->goHome(); // Back to the home page
app->userChanged()->emit(app);
} else {
app->mainWindow()->setStatusText(WString::tr("Wrong username or password"));
app->log("SECURITY") << username << " failed log in";
}
}

void LoginWindow::handleCancelHit() {
VidaApp* app = getApp();
app->mainWindow()->setStatusText(tr("Login Cancelled"));
app->goHome();
}


} // namespace vidanueva
12 changes: 5 additions & 7 deletions LoginWindow.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#define LOGIN_WINDOW_HPP

#include "MoreAwesomeTemplate.hpp"
#include "DialogButtonBar.hpp"
#include "ButtonBar.hpp"
#include <Wt/WLineEdit>
#include <string>

Expand All @@ -29,7 +29,6 @@ namespace Wt {
class WLabel;
class WContainerWidget;
class WPushButton;
class WDialog;
}

using Wt::WTemplate;
Expand All @@ -38,7 +37,6 @@ using Wt::WLabel;
using Wt::WContainerWidget;
using Wt::WPushButton;
using Wt::WLineEdit;
using Wt::WDialog;

namespace vidanueva {

Expand All @@ -48,11 +46,11 @@ namespace vidanueva {
WLineEdit* _usernameEdit;
WLabel* _passwordLabel;
WLineEdit* _passwordEdit;
DialogButtonBar* _btnBar;
ButtonBar* _btnBar;
void handleOKHit();
void handleCancelHit();
public:
LoginWindow(WDialog& parentDialog);
std::string username() { return _usernameEdit->text().toUTF8(); } /// Returns the username entered by the user
std::string password() { return _passwordEdit->text().toUTF8(); } /// Returns the password entered by the user
LoginWindow(WContainerWidget* parent=0);
void setFocus() { _usernameEdit->setFocus(); }
};

Expand Down
47 changes: 43 additions & 4 deletions MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,25 @@
#include "MainWindow.hpp"
#include "App.hpp"
#include "ControlPanel.hpp"
#include "LoginWindow.hpp"
#include <Wt/WAnchor>
#include <Wt/WString>
#include <Wt/WLogger>

using Wt::WAnchor;

namespace vidanueva {

const std::string MainWindow::pageBaseURL = "/page/"; /// The base where pages are
const std::string MainWindow::loginURL = "/login"; /// The base where pages are

void MainWindow::refresh() {
WTemplate::refresh();
VidaApp* app = getApp();
app->log("PATH") << "Main Window Refreshed with path: " << app->internalPath() << " url " << app->url() << " bookmarkURL " << app->bookmarkUrl();
onPathChange(app->internalPath());
}

/**
* @brief Builds up the display and widgets
*
Expand All @@ -34,26 +46,53 @@ namespace vidanueva {
MainWindow::MainWindow(WContainerWidget* parent) : WTemplate(parent) {
setTemplateText(tr("main-template"));
VidaApp* app = getApp();
app->log("DEBUG") << "Main Window Creating";
app->userChanged()->connect(this, &MainWindow::onUserChanged);
app->internalPathChanged().connect(this, &MainWindow::onPathChange);
onUserChanged(app);
setStatusText("");
bindString("nav", "NAV HERE");
onPathChange(app->internalPath());
}

/**
* @brief Called when a user logs in or out. Used to update the control panel
*/
void MainWindow::onUserChanged(VidaApp* app) {
if (!app->loggedIn()) {
if (!app->userSession()->isLoggedIn()) {
WAnchor* login = new WAnchor();
login->setRefInternalPath("/login");
login->setText(tr("login"));
login->setRefInternalPath(loginURL);
login->setText(tr("Login"));
bindWidget("controls", login);
} else {
setStatusText("Welcome " + app->username());
setStatusText("Welcome " + app->userSession()->username());
bindWidget("controls", new ControlPanel());
}
}

void MainWindow::onPathChange(const std::string& path) {
VidaApp* app = getApp();
std::string nextPart = app->internalPathNextPart(path);
Page* page = 0;
if ((path == "/") && nextPart.empty()) {
// Load the default/welcome page
page = app->pages().load("");
} else if (app->internalPathMatches(pageBaseURL)) {
// Look up the page that belongs here
std::string pageName = app->internalPathNextPart(pageBaseURL); // What comes after /page/ ..
page = app->pages().load(pageName);
}
if (page != 0) {
// Load a page and return
setBody(page);
return;
}
// If we didn't load a page
if (app->internalPathMatches(loginURL)) {
LoginWindow* loginWindow = new LoginWindow();
setBody(loginWindow);
}
}


} // namespace vidanueva {
Loading

0 comments on commit 3a58adc

Please sign in to comment.