From dafb065eb363e692f9d0ec64466d3b1a06cf2580 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Fri, 27 Oct 2017 17:09:55 +0200 Subject: [PATCH] Added possibility for admins to add and remove users --- source/calendarwebapp/authenticator.d | 30 ++++++++++++++-- source/calendarwebapp/calendarwebapp.d | 48 ++++++++++++++++++++----- source/calendarwebapp/configuration.d | 4 +++ test/calendarwebapp/testauthenticator.d | 3 ++ views/{create.dt => createevent.dt} | 2 +- views/createuser.dt | 32 +++++++++++++++++ views/navigation.dt | 6 +++- views/showevents.dt | 2 +- views/showusers.dt | 18 ++++++++++ 9 files changed, 130 insertions(+), 15 deletions(-) rename views/{create.dt => createevent.dt} (97%) create mode 100644 views/createuser.dt create mode 100644 views/showusers.dt diff --git a/source/calendarwebapp/authenticator.d b/source/calendarwebapp/authenticator.d index 6607f1f..cf97d60 100644 --- a/source/calendarwebapp/authenticator.d +++ b/source/calendarwebapp/authenticator.d @@ -2,14 +2,18 @@ module calendarwebapp.authenticator; import poodinis; +import std.range : InputRange; import std.typecons : nullable, Nullable; -import vibe.data.bson : Bson, BsonObjectID, deserializeBson; +import vibe.data.bson; import vibe.db.mongo.collection : MongoCollection; interface Authenticator { Nullable!AuthInfo checkUser(string username, string password) @safe; + void addUser(AuthInfo authInfo) @safe; + InputRange!AuthInfo getAllUsers() @safe; + void removeUser(BsonObjectID id) @safe; } class MongoDBAuthenticator(Collection = MongoCollection) : Authenticator @@ -35,7 +39,28 @@ public: return authInfo.nullable; } } - return Nullable!AuthInfo(); + return Nullable!AuthInfo.init; + } + + void addUser(AuthInfo authInfo) @safe + { + if (!authInfo.id.valid) + authInfo.id = BsonObjectID.generate; + + users.insert(authInfo.serializeToBson); + } + + InputRange!AuthInfo getAllUsers() @safe + { + import std.algorithm : map; + import std.range : inputRangeObject; + + return users.find().map!(deserializeBson!AuthInfo).inputRangeObject; + } + + void removeUser(BsonObjectID id) @safe + { + users.remove(["_id" : id]); } } @@ -75,5 +100,4 @@ private: } return ret; } - } diff --git a/source/calendarwebapp/calendarwebapp.d b/source/calendarwebapp/calendarwebapp.d index 8427942..7bcc1ff 100644 --- a/source/calendarwebapp/calendarwebapp.d +++ b/source/calendarwebapp/calendarwebapp.d @@ -1,6 +1,8 @@ module calendarwebapp.calendarwebapp; -import calendarwebapp.authenticator : Authenticator, AuthInfo; +import botan.rng.rng : RandomNumberGenerator; + +import calendarwebapp.authenticator : Authenticator, AuthInfo, Privilege = Role; import calendarwebapp.event; import core.time : days; @@ -23,12 +25,12 @@ import vibe.web.web : errorDisplay, noRoute, redirect, render, SessionVar, { @noRoute AuthInfo authenticate(scope HTTPServerRequest req, scope HTTPServerResponse) @safe { - if (!req.session || !req.session.isKeySet("auth")) + if (!req.session || !req.session.isKeySet("authInfo")) { redirect("/login"); return AuthInfo.init; } - return req.session.get!AuthInfo("auth"); + return req.session.get!AuthInfo("authInfo"); } public: @@ -47,7 +49,7 @@ public: { auto authInfo = authenticator.checkUser(username, password); enforce(!authInfo.isNull, "Benutzername oder Passwort ungültig"); - auth = authInfo.get; + this.authInfo = authInfo.get; redirect("/"); } @@ -57,12 +59,12 @@ public: redirect("/"); } - @anyAuth void getCreate(ValidationErrorData _error = ValidationErrorData.init) + @anyAuth void getCreateevent(ValidationErrorData _error = ValidationErrorData.init) { - render!("create.dt", _error); + render!("createevent.dt", _error); } - @anyAuth @errorDisplay!getCreate void postCreate(Date begin, + @anyAuth @errorDisplay!getCreateevent void postCreateevent(Date begin, Nullable!Date end, string description, string name, EventType type, bool shout) @safe { import std.array : replace, split; @@ -78,12 +80,39 @@ public: redirect("/"); } - @anyAuth void postRemove(BsonObjectID id) @safe + @anyAuth void postRemoveevent(BsonObjectID id) @safe { eventStore.removeEvent(id); redirect("/"); } + @auth(Role.admin) void getUsers() + { + auto users = authenticator.getAllUsers; + render!("showusers.dt", users); + } + + @auth(Role.admin) void postRemoveuser(BsonObjectID id) @safe + { + authenticator.removeUser(id); + redirect("/users"); + } + + @auth(Role.admin) void getCreateuser(ValidationErrorData _error = ValidationErrorData.init) + { + render!("createuser.dt", _error); + } + + @auth(Role.admin) @errorDisplay!getCreateuser void postCreateuser(string username, + string password, Privilege role) + { + import botan.passhash.bcrypt; + + authenticator.addUser(AuthInfo(BsonObjectID.generate, username, + generateBcrypt(password, rng, 10), role)); + redirect("/users"); + } + private: struct ValidationErrorData { @@ -91,8 +120,9 @@ private: string field; } - SessionVar!(AuthInfo, "auth") auth; + SessionVar!(AuthInfo, "authInfo") authInfo; @Autowire EventStore eventStore; @Autowire Authenticator authenticator; + @Autowire RandomNumberGenerator rng; } diff --git a/source/calendarwebapp/configuration.d b/source/calendarwebapp/configuration.d index c2c0959..6a33e86 100644 --- a/source/calendarwebapp/configuration.d +++ b/source/calendarwebapp/configuration.d @@ -1,5 +1,8 @@ module calendarwebapp.configuration; +import botan.rng.auto_rng : AutoSeededRNG; +import botan.rng.rng : RandomNumberGenerator; + import calendarwebapp.authenticator : Authenticator, MongoDBAuthenticator; import calendarwebapp.calendarwebapp : CalendarWebapp; import calendarwebapp.event : EventStore, MongoDBEventStore; @@ -19,6 +22,7 @@ public: container.register!MongoClient.existingInstance(mongoClient); container.register!(EventStore, MongoDBEventStore!()); container.register!(Authenticator, MongoDBAuthenticator!()); + container.register!(RandomNumberGenerator, AutoSeededRNG); container.register!CalendarWebapp; container.register!(ValueInjector!string, StringInjector); container.register!(ValueInjector!MongoCollection, MongoCollectionInjector); diff --git a/test/calendarwebapp/testauthenticator.d b/test/calendarwebapp/testauthenticator.d index 48addc1..c672b09 100644 --- a/test/calendarwebapp/testauthenticator.d +++ b/test/calendarwebapp/testauthenticator.d @@ -11,7 +11,10 @@ import vibe.data.bson : Bson, BsonObjectID; interface Collection { + Bson[] find() @safe; Bson findOne(string[string] query) @safe; + void insert(Bson document) @safe; + void remove(BsonObjectID[string] selector) @safe; } class CollectionInjector : ValueInjector!Collection diff --git a/views/create.dt b/views/createevent.dt similarity index 97% rename from views/create.dt rename to views/createevent.dt index 19f8e53..d3ec627 100644 --- a/views/create.dt +++ b/views/createevent.dt @@ -3,7 +3,7 @@ block content - void showerror(string field = null) - if (_error.msg && _error.field == field) td.error= _error.msg - form(action="/create", method="post") + form(action="/createevent", method="post") fieldset(name="eventFields") table tbody#fieldTable diff --git a/views/createuser.dt b/views/createuser.dt new file mode 100644 index 0000000..79c9670 --- /dev/null +++ b/views/createuser.dt @@ -0,0 +1,32 @@ +extends layout +block content + - void showerror(string field = null) + - if (_error.msg && _error.field == field) + td.error= _error.msg + form(action="/createuser", method="post") + fieldset(name="eventFields") + table + tbody#fieldTable + tr + td + label(for="username") Benutzername + td + input#username(value="", name="username", type="text") + - showerror("username"); + tr + td + label(for="password") Passwort + td + input#password(value="", name="password", type="password") + tr + td + label(for="role") Rolle + td + select#type(name="role") + option(value="User") Benutzer + option(value="Admin") Administrator + - showerror("role"); + tfoot + tr + td(colspan="2") + input#submitButton(type="submit", value="Ereignis erstellen") \ No newline at end of file diff --git a/views/navigation.dt b/views/navigation.dt index e7719aa..bec6532 100644 --- a/views/navigation.dt +++ b/views/navigation.dt @@ -3,6 +3,10 @@ nav li a(href='/') Home li - a(href='/create') Ereignis erstellen + a(href='/createevent') Ereignis erstellen + li + a(href='/users') Benutzerliste + li + a(href='/createuser') Benutzer erstellen li a(href='/logout') Ausloggen \ No newline at end of file diff --git a/views/showevents.dt b/views/showevents.dt index 8592840..59e2b5c 100644 --- a/views/showevents.dt +++ b/views/showevents.dt @@ -24,7 +24,7 @@ block content tr td shout td #{event.shout} - form(action="/remove", method="post") + form(action="/removeevent", method="post") input#id(value="#{event.id}", name="id", type="hidden") input#submitButton(type="submit", value="Entfernen") hr diff --git a/views/showusers.dt b/views/showusers.dt new file mode 100644 index 0000000..749dbae --- /dev/null +++ b/views/showusers.dt @@ -0,0 +1,18 @@ +extends layout.dt +block content + h1 Users + - foreach (user; users) + table + tr + td id + td #{user.id} + tr + td username + td #{user.username} + tr + td role + td #{user.role} + form(action="/removeuser", method="post") + input#id(value="#{user.id}", name="id", type="hidden") + input#submitButton(type="submit", value="Entfernen") + hr