diff --git a/source/calendarwebapp/authenticator.d b/source/calendarwebapp/authenticator.d index 8f44335..5f79986 100644 --- a/source/calendarwebapp/authenticator.d +++ b/source/calendarwebapp/authenticator.d @@ -1,5 +1,7 @@ module calendarwebapp.authenticator; +import calendarwebapp.passhash : PasswordHasher; + import poodinis; import std.range : InputRange; @@ -21,6 +23,7 @@ class MongoDBAuthenticator(Collection = MongoCollection) : Authenticator private: @Value("users") Collection users; + @Autowire PasswordHasher passwordHasher; public: Nullable!AuthInfo checkUser(string username, string password) @safe @@ -28,13 +31,13 @@ public: import botan.passhash.bcrypt : checkBcrypt; 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 blocking this */ if (result != Bson(null)) { auto authInfo = result.deserializeBson!AuthInfo; - if ((()@trusted => checkBcrypt(password, authInfo.passwordHash))()) + if (passwordHasher.checkHash(password, authInfo.passwordHash)) { return authInfo.nullable; } diff --git a/source/calendarwebapp/calendarwebapp.d b/source/calendarwebapp/calendarwebapp.d index 38efaae..5757bfb 100644 --- a/source/calendarwebapp/calendarwebapp.d +++ b/source/calendarwebapp/calendarwebapp.d @@ -1,9 +1,8 @@ module calendarwebapp.calendarwebapp; -import botan.rng.rng : RandomNumberGenerator; - import calendarwebapp.authenticator; import calendarwebapp.event; +import calendarwebapp.passhash : PasswordHasher; import core.time : days; @@ -108,12 +107,10 @@ public: } @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, - generateBcrypt(password, rng, 10), role)); + passwordHasher.generateHash(password), role)); redirect("/users"); } @@ -129,5 +126,5 @@ private: @Autowire EventStore eventStore; @Autowire Authenticator authenticator; - @Autowire RandomNumberGenerator rng; + @Autowire PasswordHasher passwordHasher; } diff --git a/source/calendarwebapp/configuration.d b/source/calendarwebapp/configuration.d index 6a33e86..21a5465 100644 --- a/source/calendarwebapp/configuration.d +++ b/source/calendarwebapp/configuration.d @@ -6,6 +6,7 @@ import botan.rng.rng : RandomNumberGenerator; import calendarwebapp.authenticator : Authenticator, MongoDBAuthenticator; import calendarwebapp.calendarwebapp : CalendarWebapp; import calendarwebapp.event : EventStore, MongoDBEventStore; +import calendarwebapp.passhash : BcryptPasswordHasher, PasswordHasher; import poodinis; @@ -22,6 +23,7 @@ public: container.register!MongoClient.existingInstance(mongoClient); container.register!(EventStore, MongoDBEventStore!()); container.register!(Authenticator, MongoDBAuthenticator!()); + container.register!(PasswordHasher, BcryptPasswordHasher); container.register!(RandomNumberGenerator, AutoSeededRNG); container.register!CalendarWebapp; container.register!(ValueInjector!string, StringInjector); diff --git a/source/calendarwebapp/passhash.d b/source/calendarwebapp/passhash.d new file mode 100644 index 0000000..1cdb57a --- /dev/null +++ b/source/calendarwebapp/passhash.d @@ -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; +}