Added possibility for admins to add and remove users
This commit is contained in:
parent
026b3f993e
commit
dafb065eb3
9 changed files with 130 additions and 15 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
32
views/createuser.dt
Normal 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")
|
|
@ -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
|
|
@ -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
18
views/showusers.dt
Normal 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
|
Loading…
Reference in a new issue