Merge branch 'master' into JSON-export

This commit is contained in:
Johannes Loher 2017-12-09 23:34:21 +01:00
commit 29a20fa4fb
4 changed files with 131 additions and 43 deletions

View file

@ -30,14 +30,15 @@ private:
public: public:
Nullable!AuthInfo checkUser(string username, string password) @safe Nullable!AuthInfo checkUser(string username, string password) @safe
{ {
auto result = users.findOne(["username" : username]); import vibe.core.concurrency : async;
/* checkHash should be called using vibe.core.concurrency.async to
avoid blocking, but https://github.com/vibe-d/vibe.d/issues/1521 is immutable result = users.findOne(["username" : username]);
blocking this */
if (result != Bson(null)) if (result != Bson(null))
{ {
auto authInfo = result.deserializeBson!AuthInfo; auto authInfo = result.deserializeBson!AuthInfo;
if (passwordHasher.checkHash(password, authInfo.passwordHash)) if ((()@trusted => async(() => passwordHasher.checkHash(password,
authInfo.passwordHash)).getResult)())
{ {
return authInfo.nullable; return authInfo.nullable;
} }
@ -90,15 +91,19 @@ private:
@Autowire MySQLPool pool; @Autowire MySQLPool pool;
@Autowire PasswordHasher passwordHasher; @Autowire PasswordHasher passwordHasher;
@Value("mysql.table.users") string usersTableName;
public: public:
Nullable!AuthInfo checkUser(string username, string password) @trusted Nullable!AuthInfo checkUser(string username, string password) @trusted
{ {
import vibe.core.concurrency : async;
auto cn = pool.lockConnection(); auto cn = pool.lockConnection();
scope (exit) scope (exit)
cn.close(); cn.close();
auto prepared = cn.prepare( auto prepared = cn.prepare(
"SELECT id, username, passwordHash, privilege FROM users WHERE username = ?"); "SELECT id, username, passwordHash, privilege FROM "
~ usersTableName ~ " WHERE username = ?");
prepared.setArg(0, username); prepared.setArg(0, username);
auto result = prepared.query(); auto result = prepared.query();
/* checkHash should be called using vibe.core.concurrency.async to /* checkHash should be called using vibe.core.concurrency.async to
@ -107,7 +112,7 @@ public:
if (!result.empty) if (!result.empty)
{ {
auto authInfo = toAuthInfo(result.front); auto authInfo = toAuthInfo(result.front);
if (passwordHasher.checkHash(password, authInfo.passwordHash)) if (async(() => passwordHasher.checkHash(password, authInfo.passwordHash)).getResult)
{ {
return authInfo.nullable; return authInfo.nullable;
} }
@ -121,7 +126,7 @@ public:
scope (exit) scope (exit)
cn.close; cn.close;
auto prepared = cn.prepare( auto prepared = cn.prepare(
"INSERT INTO users (username, passwordHash, privilege) VALUES(?, ?, ?)"); "INSERT INTO " ~ usersTableName ~ " (username, passwordHash, privilege) VALUES(?, ?, ?)");
prepared.setArgs(authInfo.username, authInfo.passwordHash, authInfo.privilege.to!uint); prepared.setArgs(authInfo.username, authInfo.passwordHash, authInfo.privilege.to!uint);
prepared.exec(); prepared.exec();
} }
@ -134,7 +139,7 @@ public:
auto cn = pool.lockConnection(); auto cn = pool.lockConnection();
scope (exit) scope (exit)
cn.close; cn.close;
auto prepared = cn.prepare("SELECT id, username, passwordHash, privilege FROM users"); auto prepared = cn.prepare("SELECT id, username, passwordHash, privilege FROM " ~ usersTableName ~ "");
return prepared.querySet.map!(r => toAuthInfo(r)).inputRangeObject; return prepared.querySet.map!(r => toAuthInfo(r)).inputRangeObject;
} }
@ -143,14 +148,14 @@ public:
auto cn = pool.lockConnection(); auto cn = pool.lockConnection();
scope (exit) scope (exit)
cn.close; cn.close;
auto prepared = cn.prepare("DELETE FROM users WHERE id = ?"); auto prepared = cn.prepare("DELETE FROM " ~ usersTableName ~ " WHERE id = ?");
prepared.setArg(0, id.to!uint); prepared.setArg(0, id.to!uint);
prepared.exec(); prepared.exec();
} }
private: private:
AuthInfo toAuthInfo(Row r) AuthInfo toAuthInfo(in Row r)
{ {
import std.conv : to; import std.conv : to;

View file

@ -65,7 +65,6 @@ public:
render!("createevent.dt", _error, authInfo); render!("createevent.dt", _error, authInfo);
} }
@auth(Role.user | Role.admin) @errorDisplay!getCreateevent void postCreateevent(Date begin, @auth(Role.user | Role.admin) @errorDisplay!getCreateevent void postCreateevent(Date begin,
Nullable!Date end, string description, string name, EventType type, bool shout) Nullable!Date end, string description, string name, EventType type, bool shout)
{ {
@ -109,8 +108,10 @@ public:
@auth(Role.admin) @errorDisplay!getCreateuser void postCreateuser(string username, @auth(Role.admin) @errorDisplay!getCreateuser void postCreateuser(string username,
string password, Privilege role) string password, Privilege role)
{ {
import vibe.core.concurrency : async;
authenticator.addUser(AuthInfo("", username, authenticator.addUser(AuthInfo("", username,
passwordHasher.generateHash(password), role)); async(() => passwordHasher.generateHash(password)).getResult, role));
redirect("/users"); redirect("/users");
} }
@ -121,8 +122,8 @@ private:
string field; string field;
} }
SessionVar!(AuthInfo, "authInfo") authInfo = AuthInfo("", SessionVar!(AuthInfo, "authInfo") authInfo = AuthInfo("", string.init,
string.init, string.init, Privilege.None); string.init, Privilege.None);
@Autowire EventStore eventStore; @Autowire EventStore eventStore;
@Autowire Authenticator authenticator; @Autowire Authenticator authenticator;

View file

@ -1,34 +1,53 @@
module calendarwebapp.configuration; module calendarwebapp.configuration;
import calendarwebapp.authenticator : Authenticator, MongoDBAuthenticator, import calendarwebapp.authenticator : Authenticator;
MySQLAuthenticator;
import calendarwebapp.calendarwebapp : CalendarWebapp; import calendarwebapp.calendarwebapp : CalendarWebapp;
import calendarwebapp.event : EventStore, MongoDBEventStore, MySQLEventStore; import calendarwebapp.event : EventStore;
import calendarwebapp.passhash : PasswordHasher, SHA256PasswordHasher; import calendarwebapp.passhash : PasswordHasher, SHA256PasswordHasher;
import mysql : MySQLPool;
import poodinis; import poodinis;
import vibe.db.mongo.client : MongoClient; import vibe.core.log : logInfo;
import vibe.db.mongo.collection : MongoCollection; import vibe.db.mongo.collection : MongoCollection;
import vibe.db.mongo.mongo : connectMongoDB;
class Context : ApplicationContext class Context : ApplicationContext
{ {
public: public:
override void registerDependencies(shared(DependencyContainer) container) override void registerDependencies(shared(DependencyContainer) container)
{ {
auto mongoClient = connectMongoDB("localhost"); container.register!(ValueInjector!Arguments, AppArgumentsInjector);
auto pool = new MySQLPool("localhost", "username", "password", "CalendarWebapp"); auto arguments = container.resolve!(AppArgumentsInjector).get("");
container.register!MySQLPool.existingInstance(pool); final switch (arguments.database) with (DatabaseArgument)
container.register!MongoClient.existingInstance(mongoClient); {
container.register!(EventStore, MySQLEventStore); case mongodb:
container.register!(Authenticator, MySQLAuthenticator); import vibe.db.mongo.client : MongoClient;
import vibe.db.mongo.mongo : connectMongoDB;
import calendarwebapp.authenticator : MongoDBAuthenticator;
import calendarwebapp.event : MongoDBEventStore;
auto mongoClient = connectMongoDB(arguments.mongodb.host);
container.register!MongoClient.existingInstance(mongoClient);
container.register!(EventStore, MongoDBEventStore!());
container.register!(Authenticator, MongoDBAuthenticator!());
container.register!(ValueInjector!MongoCollection, MongoCollectionInjector);
logInfo("Using MongoDB as database system");
break;
case mysql:
import mysql : MySQLPool;
import calendarwebapp.authenticator : MySQLAuthenticator;
import calendarwebapp.event : MySQLEventStore;
auto pool = new MySQLPool(arguments.mysql.host, arguments.mysql.username,
arguments.mysql.password, arguments.mysql.database);
container.register!MySQLPool.existingInstance(pool);
container.register!(EventStore, MySQLEventStore);
container.register!(Authenticator, MySQLAuthenticator);
logInfo("Using MySQL as database system");
break;
}
container.register!(PasswordHasher, SHA256PasswordHasher); container.register!(PasswordHasher, SHA256PasswordHasher);
container.register!CalendarWebapp; container.register!CalendarWebapp;
container.register!(ValueInjector!string, StringInjector); container.register!(ValueInjector!string, StringInjector);
container.register!(ValueInjector!MongoCollection, MongoCollectionInjector);
} }
} }
@ -36,19 +55,18 @@ class StringInjector : ValueInjector!string
{ {
private: private:
string[string] config; string[string] config;
@Value() Arguments arguments;
bool initialized = false;
public: public:
this() const @safe pure nothrow
{
// dfmt off
config = ["Database name" : "CalendarWebapp",
"Users collection name": "users",
"Events collection name" : "events"];
// dfmt on
}
override string get(string key) const @safe pure nothrow override string get(string key) @safe nothrow
{ {
if (!initialized)
{
config = ["MongoDB database name" : arguments.mongodb.database,
"mysql.table.users" : "users", "mysql.table.events" : "events"];
}
return config[key]; return config[key];
} }
} }
@ -56,8 +74,10 @@ public:
class MongoCollectionInjector : ValueInjector!MongoCollection class MongoCollectionInjector : ValueInjector!MongoCollection
{ {
private: private:
import vibe.db.mongo.client : MongoClient;
@Autowire MongoClient mongoClient; @Autowire MongoClient mongoClient;
@Value("Database name") @Value("MongoDB database name")
string databaseName; string databaseName;
public: public:
@ -66,3 +86,63 @@ public:
return mongoClient.getCollection(databaseName ~ "." ~ key); return mongoClient.getCollection(databaseName ~ "." ~ key);
} }
} }
class AppArgumentsInjector : ValueInjector!Arguments
{
private:
Arguments arguments;
public:
this()
{
import vibe.core.args : readOption;
readOption("database", &arguments.database, "The database system to use.");
readOption("mongodb.host", &arguments.mongodb.host,
"The host of the MongoDB instance to use.");
readOption("mongodb.database", &arguments.mongodb.database,
"The name of the MongoDB database to use.");
readOption("mysql.host", &arguments.mysql.host, "The host of the MySQL instance to use.");
readOption("mysql.username", &arguments.mysql.username,
"The username to use for logging into the MySQL instance.");
readOption("mysql.password", &arguments.mysql.password,
"The password to use for logging into the MySQL instance.");
readOption("mysql.database", &arguments.mysql.database,
"The name of the MySQL database to use.");
}
override Arguments get(string key) @safe
{
import std.exception : enforce;
enforce(key == "", "There is only one instance of Arguments, to inject it use @Value().");
return arguments;
}
}
enum DatabaseArgument
{
mongodb,
mysql
}
struct MySQLArguments
{
string host = "localhost";
string username = "username";
string password = "password";
string database = "CalendarWebapp";
}
struct MongoDBArguments
{
string host = "localhost";
string database = "CalendarWebapp";
}
struct Arguments
{
DatabaseArgument database = DatabaseArgument.mongodb;
MySQLArguments mysql;
MongoDBArguments mongodb;
}

View file

@ -73,6 +73,8 @@ class MySQLEventStore : EventStore
private: private:
import mysql; import mysql;
@Value("mysql.table.events") string eventsTableName;
public: public:
Event getEvent(string id) Event getEvent(string id)
{ {
@ -80,7 +82,7 @@ public:
scope (exit) scope (exit)
cn.close; cn.close;
auto prepared = cn.prepare( auto prepared = cn.prepare(
"SELECT id begin end name description type shout FROM events WHERE id = ?"); "SELECT id begin end name description type shout FROM " ~ eventsTableName ~ " WHERE id = ?");
prepared.setArg(0, id.to!uint); prepared.setArg(0, id.to!uint);
return toEvent(prepared.query.front); return toEvent(prepared.query.front);
} }
@ -91,7 +93,7 @@ public:
scope (exit) scope (exit)
cn.close; cn.close;
auto prepared = cn.prepare( auto prepared = cn.prepare(
"SELECT id, begin, end, name, description, type, shout FROM events"); "SELECT id, begin, end, name, description, type, shout FROM " ~ eventsTableName ~ "");
return prepared.querySet.map!(r => toEvent(r)).inputRangeObject; return prepared.querySet.map!(r => toEvent(r)).inputRangeObject;
} }
@ -101,7 +103,7 @@ public:
scope (exit) scope (exit)
cn.close; cn.close;
auto prepared = cn.prepare( auto prepared = cn.prepare(
"INSERT INTO events (begin, end, name, description, type, shout) VALUES(?, ?, ?, ?, ?, ?)"); "INSERT INTO " ~ eventsTableName ~ " (begin, end, name, description, type, shout) VALUES(?, ?, ?, ?, ?, ?)");
prepared.setArgs(event.begin, event.end, event.name, event.description, prepared.setArgs(event.begin, event.end, event.name, event.description,
event.type.to!uint, event.shout); event.type.to!uint, event.shout);
prepared.exec(); prepared.exec();
@ -123,7 +125,7 @@ public:
auto cn = pool.lockConnection(); auto cn = pool.lockConnection();
scope (exit) scope (exit)
cn.close; cn.close;
auto prepared = cn.prepare("DELETE FROM events WHERE id = ?"); auto prepared = cn.prepare("DELETE FROM " ~ eventsTableName ~ " WHERE id = ?");
prepared.setArg(0, id.to!uint); prepared.setArg(0, id.to!uint);
prepared.exec(); prepared.exec();
} }