Ich kenne was, was Du nicht kennst: MustBe

Im ersten Teil der "Ich kenne was..."-Reihe ging es darum, wie man mit Hilfe des Moduls Passport einer auf Express basierenden Webanwendung Authentifizierung beibringt. Nun soll ein weiteres Modul das Ganze um Autorisierung erweitern.

In Pocket speichern vorlesen Druckansicht
Lesezeit: 5 Min.
Von
  • Philip Ackermann
Inhaltsverzeichnis

Im ersten Teil der "Ich kenne was..."-Reihe ging es darum, wie man mit Hilfe des Moduls Passport einer auf Express basierenden Webanwendung Authentifizierung beibringt. Nun soll ein weiteres Modul das Ganze um Autorisierung erweitern.

Während bei der Authentifizierung die Identität von Nutzern nachgewiesen wird, dient die Autorisierung bekanntlich dazu, zu entscheiden, ob Nutzer Zugang zu bestimmten Ressourcen oder Diensten haben. Wie auch schon für die Authentifizierung bietet Express jedoch von Haus aus keine Mittel für die Autorisierung an. Ein Modul, welches dies nachholt, ist das leichtgewichtige MustBe.

Prinzipiell funktioniert MustBe sowohl mit Connect als auch mit Express, wobei im folgenden neben den Kernprinzipien von MustBe die Integration in Express kurz vorgestellt werden soll.

Installiert wird das Modul wie für Node.js gewohnt über den Package Manager npm, wobei über den Parameter --save dafür gesorgt wird, dass das Modul direkt als Abhängigkeit in die Konfigurationsdatei package.json geschrieben wird:

npm install mustbe --save

Der nächste Schritt ist es, das Modul per require() in die Anwendung einzubinden:

var mustBe = require("mustbe");

Die Konfiguration von MustBe geschieht über die Methode configure(). Diese erwartet als Parameter eine Callback-Funktion, die ihrerseits mit einem Konfigurationsobjekt als Parameter aufgerufen wird. Der Übersicht halber ist es dabei ratsam, diese Callback-Funktion in einer separaten Datei zu definieren und ebenfalls per require() einzubinden:

var mustBeConfig = require("./mustbe-config");
mustBe.configure(mustBeConfig);

Das Grundgerüst für eine solche MustBe-Konfigurationsdatei sieht wie folgt aus:

module.exports = function(config) {
// Konfiguration
};

Innerhalb dieser Callback-Funktion kann nun das als Parameter übergebene config-Objekt dazu genutzt werden, MustBe bzw. die Autorisierung entsprechend den eigenen Wünschen anzupassen. Im Wesentlichen definiert man dabei folgende Dinge:

MustBe gibt einem als Entwickler nicht vor, welches Authentifizierungsverfahren verwendet wird, sondern stellt es frei, selbst das passende Verfahren auszuwählen und zu integrieren. Eine Möglichkeit, dies zu tun, stellt die Methode userIdentity() dar. Sie bekommt als Parameter ein Identitätsobjekt übergeben, über das genau darauf Einfluss genommen werden kann, wann Nutzer authentifiziert sind und wann nicht.

config.userIdentity(function(identity){
identity.isAuthenticated(function(user, callback) {
var isAuthenticated = false;
if (user) {
isAuthenticated = user.isLoggedIn();
}
callback(null, isAuthenticated);
});
});

Prinzipiell wäre dies also die Stelle, an der beispielsweise auch Passport integriert werden könnte.

Im Gegensatz zu anderen Autorisierung-Modulen wie Node ACL sind in MustBe autorisierungsdürftige Aktionen nicht direkt an Rollen gebunden, sondern über sogenannte Aktivitäten von diesen entkoppelt. Die genauen Hintergründe dazu hat der Entwickler von MustBe (bereits Jahre vor Erscheinen des Moduls) in einem Blog-Beitrag ausführlich erläutert.

Aktivitäten werden innerhalb der Konfiguration über den Aufruf von config.activities() definiert. Folgender Code beispielsweise sorgt dafür, dass die Aktivität items.view nur von authentifizierten Nutzern ausgeführt werden darf.

config.activities(function(activities){
activities.can("items.view", function(identity, params, callback) {
identity.isAuthenticated(function(err, isAuth){
return callback(err, isAuth);
});
});
});

Prinzipiell lassen sich hierbei – sofern man denn möchte – auch Rollen verwenden:

config.activities(function(activities){
activities.can("admin", function(identity, params, callback) {
var user = identity.user;
var isAdmin = (user.roles.indexOf("admin") > -1);
callback(null, isAdmin);
});
});

Verschiedene Aspekte wie beispielsweise das Laden des Nutzerobjekts aus dem aktuellen Request und das Behandeln von Standardfällen wie gescheiterte Authentifizierung und gescheiterte Autorisierung lassen sich über entsprechende Hilfsmethoden konfigurieren:

config.routeHelpers(function(routeHelpers){
/* ... */

routeHelpers.notAuthorized(function(req, res, next){
res.redirect("/login");
});

routeHelpers.notAuthenticated(function(req, res, next){
res.redirect("/login");
});

/* ... */
});

Nach der Konfiguration von MustBe ist die Integration in Express nun vergleichsweise einfach. Dazu wird der entsprechenden Routendefinition lediglich ein Aufruf von mustBe.authorized() vorgeschaltet. Statt also zu schreiben

var router = new express.Router();
router.get("/", function (req, res, next) {
res.render("items");
}
);

ergänzt man die Autorisierung wie folgt:

var router = new express.Router();
router.get("/", mustBe.authorized("items.view", function (req, res, next) {
res.render("items");
}
)
);

Dies sorgt nun dafür, dass vor Rendern der Seite items zunächst überprüft wird, ob der angemeldete Nutzer (bzw. allgemeiner: die angemeldete Identität) die Rechte hat, die Aktivität items.view auszuführen.

Alternativ dazu lässt sich MustBe auch als Middleware einsetzen. Dies ist beispielsweise immer dann sinnvoll, wenn man eine Aktivität zu einem ganzen Subtree von Routen (beispielsweise items/*) zuordnen möchte. Erreicht werden kann das über die Methode routeHelpers() wie folgt:

app.use("/items", mustBe.routeHelpers().authorized("items.view", 
function(req, res, next) {
next();
}
));

Das Modul MustBe lässt sich relativ einfach in das Framework Express integrieren und erweitert es um Möglichkeiten zur Autorisierung. MustBe führt mit Aktivitäten eine zusätzliche Abstraktionsschicht ein, sodass der Code an dieser Stelle frei von Rollendefinitionen bleiben kann (aber es nicht muss). Bezüglich der Authentifizierung gibt MustBe keine Vorgaben und ermöglicht prinzipiell die Integration beliebiger Authentifizierungsmodule. ()