Added possibility for admins to add and remove users

This commit is contained in:
Johannes Loher 2017-10-27 17:09:55 +02:00
parent 026b3f993e
commit dafb065eb3
9 changed files with 130 additions and 15 deletions

View file

@ -2,14 +2,18 @@ module calendarwebapp.authenticator;
import poodinis; import poodinis;
import std.range : InputRange;
import std.typecons : nullable, Nullable; import std.typecons : nullable, Nullable;
import vibe.data.bson : Bson, BsonObjectID, deserializeBson; import vibe.data.bson;
import vibe.db.mongo.collection : MongoCollection; import vibe.db.mongo.collection : MongoCollection;
interface Authenticator interface Authenticator
{ {
Nullable!AuthInfo checkUser(string username, string password) @safe; 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 class MongoDBAuthenticator(Collection = MongoCollection) : Authenticator
@ -35,7 +39,28 @@ public:
return authInfo.nullable; 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; return ret;
} }
} }

View file

@ -1,6 +1,8 @@
module calendarwebapp.calendarwebapp; module calendarwebapp.calendarwebapp;
import calendarwebapp.authenticator : Authenticator, AuthInfo; import botan.rng.rng : RandomNumberGenerator;
import calendarwebapp.authenticator : Authenticator, AuthInfo, Privilege = Role;
import calendarwebapp.event; import calendarwebapp.event;
import core.time : days; 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 @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"); redirect("/login");
return AuthInfo.init; return AuthInfo.init;
} }
return req.session.get!AuthInfo("auth"); return req.session.get!AuthInfo("authInfo");
} }
public: public:
@ -47,7 +49,7 @@ public:
{ {
auto authInfo = authenticator.checkUser(username, password); auto authInfo = authenticator.checkUser(username, password);
enforce(!authInfo.isNull, "Benutzername oder Passwort ungültig"); enforce(!authInfo.isNull, "Benutzername oder Passwort ungültig");
auth = authInfo.get; this.authInfo = authInfo.get;
redirect("/"); redirect("/");
} }
@ -57,12 +59,12 @@ public:
redirect("/"); 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 Nullable!Date end, string description, string name, EventType type, bool shout) @safe
{ {
import std.array : replace, split; import std.array : replace, split;
@ -78,12 +80,39 @@ public:
redirect("/"); redirect("/");
} }
@anyAuth void postRemove(BsonObjectID id) @safe @anyAuth void postRemoveevent(BsonObjectID id) @safe
{ {
eventStore.removeEvent(id); eventStore.removeEvent(id);
redirect("/"); 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: private:
struct ValidationErrorData struct ValidationErrorData
{ {
@ -91,8 +120,9 @@ private:
string field; string field;
} }
SessionVar!(AuthInfo, "auth") auth; SessionVar!(AuthInfo, "authInfo") authInfo;
@Autowire EventStore eventStore; @Autowire EventStore eventStore;
@Autowire Authenticator authenticator; @Autowire Authenticator authenticator;
@Autowire RandomNumberGenerator rng;
} }

View file

@ -1,5 +1,8 @@
module calendarwebapp.configuration; module calendarwebapp.configuration;
import botan.rng.auto_rng : AutoSeededRNG;
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;
@ -19,6 +22,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!(RandomNumberGenerator, AutoSeededRNG);
container.register!CalendarWebapp; container.register!CalendarWebapp;
container.register!(ValueInjector!string, StringInjector); container.register!(ValueInjector!string, StringInjector);
container.register!(ValueInjector!MongoCollection, MongoCollectionInjector); container.register!(ValueInjector!MongoCollection, MongoCollectionInjector);

View file

@ -11,7 +11,10 @@ import vibe.data.bson : Bson, BsonObjectID;
interface Collection interface Collection
{ {
Bson[] find() @safe;
Bson findOne(string[string] query) @safe; Bson findOne(string[string] query) @safe;
void insert(Bson document) @safe;
void remove(BsonObjectID[string] selector) @safe;
} }
class CollectionInjector : ValueInjector!Collection class CollectionInjector : ValueInjector!Collection

View file

@ -3,7 +3,7 @@ block content
- void showerror(string field = null) - void showerror(string field = null)
- if (_error.msg && _error.field == field) - if (_error.msg && _error.field == field)
td.error= _error.msg td.error= _error.msg
form(action="/create", method="post") form(action="/createevent", method="post")
fieldset(name="eventFields") fieldset(name="eventFields")
table table
tbody#fieldTable tbody#fieldTable

32
views/createuser.dt Normal file
View file

@ -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")

View file

@ -3,6 +3,10 @@ nav
li li
a(href='/') Home a(href='/') Home
li li
a(href='/create') Ereignis erstellen a(href='/createevent') Ereignis erstellen
li
a(href='/users') Benutzerliste
li
a(href='/createuser') Benutzer erstellen
li li
a(href='/logout') Ausloggen a(href='/logout') Ausloggen

View file

@ -24,7 +24,7 @@ block content
tr tr
td shout td shout
td #{event.shout} td #{event.shout}
form(action="/remove", method="post") form(action="/removeevent", method="post")
input#id(value="#{event.id}", name="id", type="hidden") input#id(value="#{event.id}", name="id", type="hidden")
input#submitButton(type="submit", value="Entfernen") input#submitButton(type="submit", value="Entfernen")
hr hr

18
views/showusers.dt Normal file
View file

@ -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