added abstraction layer for password hashing

This commit is contained in:
Johannes Loher 2017-10-27 18:58:46 +02:00
parent 0143b933d5
commit ec0c7a64b5
4 changed files with 40 additions and 9 deletions

View file

@ -1,5 +1,7 @@
module calendarwebapp.authenticator; module calendarwebapp.authenticator;
import calendarwebapp.passhash : PasswordHasher;
import poodinis; import poodinis;
import std.range : InputRange; import std.range : InputRange;
@ -21,6 +23,7 @@ class MongoDBAuthenticator(Collection = MongoCollection) : Authenticator
private: private:
@Value("users") @Value("users")
Collection users; Collection users;
@Autowire PasswordHasher passwordHasher;
public: public:
Nullable!AuthInfo checkUser(string username, string password) @safe Nullable!AuthInfo checkUser(string username, string password) @safe
@ -28,13 +31,13 @@ public:
import botan.passhash.bcrypt : checkBcrypt; import botan.passhash.bcrypt : checkBcrypt;
auto result = users.findOne(["username" : username]); auto result = users.findOne(["username" : username]);
/* checkBcrypt should be called using vibe.core.concurrency.async to /* checkHash should be called using vibe.core.concurrency.async to
avoid blocking, but https://github.com/vibe-d/vibe.d/issues/1521 is avoid blocking, but https://github.com/vibe-d/vibe.d/issues/1521 is
blocking this */ blocking this */
if (result != Bson(null)) if (result != Bson(null))
{ {
auto authInfo = result.deserializeBson!AuthInfo; auto authInfo = result.deserializeBson!AuthInfo;
if ((()@trusted => checkBcrypt(password, authInfo.passwordHash))()) if (passwordHasher.checkHash(password, authInfo.passwordHash))
{ {
return authInfo.nullable; return authInfo.nullable;
} }

View file

@ -1,9 +1,8 @@
module calendarwebapp.calendarwebapp; module calendarwebapp.calendarwebapp;
import botan.rng.rng : RandomNumberGenerator;
import calendarwebapp.authenticator; import calendarwebapp.authenticator;
import calendarwebapp.event; import calendarwebapp.event;
import calendarwebapp.passhash : PasswordHasher;
import core.time : days; import core.time : days;
@ -108,12 +107,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) @safe
{ {
import botan.passhash.bcrypt;
authenticator.addUser(AuthInfo(BsonObjectID.generate, username, authenticator.addUser(AuthInfo(BsonObjectID.generate, username,
generateBcrypt(password, rng, 10), role)); passwordHasher.generateHash(password), role));
redirect("/users"); redirect("/users");
} }
@ -129,5 +126,5 @@ private:
@Autowire EventStore eventStore; @Autowire EventStore eventStore;
@Autowire Authenticator authenticator; @Autowire Authenticator authenticator;
@Autowire RandomNumberGenerator rng; @Autowire PasswordHasher passwordHasher;
} }

View file

@ -6,6 +6,7 @@ import botan.rng.rng : RandomNumberGenerator;
import calendarwebapp.authenticator : Authenticator, MongoDBAuthenticator; import calendarwebapp.authenticator : Authenticator, MongoDBAuthenticator;
import calendarwebapp.calendarwebapp : CalendarWebapp; import calendarwebapp.calendarwebapp : CalendarWebapp;
import calendarwebapp.event : EventStore, MongoDBEventStore; import calendarwebapp.event : EventStore, MongoDBEventStore;
import calendarwebapp.passhash : BcryptPasswordHasher, PasswordHasher;
import poodinis; import poodinis;
@ -22,6 +23,7 @@ public:
container.register!MongoClient.existingInstance(mongoClient); container.register!MongoClient.existingInstance(mongoClient);
container.register!(EventStore, MongoDBEventStore!()); container.register!(EventStore, MongoDBEventStore!());
container.register!(Authenticator, MongoDBAuthenticator!()); container.register!(Authenticator, MongoDBAuthenticator!());
container.register!(PasswordHasher, BcryptPasswordHasher);
container.register!(RandomNumberGenerator, AutoSeededRNG); container.register!(RandomNumberGenerator, AutoSeededRNG);
container.register!CalendarWebapp; container.register!CalendarWebapp;
container.register!(ValueInjector!string, StringInjector); container.register!(ValueInjector!string, StringInjector);

View file

@ -0,0 +1,29 @@
module calendarwebapp.passhash;
import poodinis;
interface PasswordHasher
{
string generateHash(in string password) @safe;
bool checkHash(in string password, in string hash) @safe;
}
class BcryptPasswordHasher : PasswordHasher
{
import botan.passhash.bcrypt : checkBcrypt, generateBcrypt;
import botan.rng.rng : RandomNumberGenerator;
string generateHash(in string password) @safe
{
return (() @trusted => generateBcrypt(password, rng, cost))();
}
bool checkHash(in string password, in string hash) @safe
{
return (()@trusted => checkBcrypt(password, hash))();
}
private:
@Autowire RandomNumberGenerator rng;
enum cost = 10;
}