Why Did I Develop Kindergarten?

I am a very busy man! I have a full-time job and 2 kids waiting for me at home, therefore I don’t really have time to code in my spare time, but I did find a time to create Kindergarten for a good reason. Yeah, of course I know there are already more JavaScript libraries than necessary..

Initially Kindergarten was developed by Hartog de Mik as ruby library when we used to work at OrganisedMinds. I was pretty excited about Kindergarten, because it helped us to make our complicated authorization logic more transparent. Before Kindergarten we used cancan and kept the authorization rules in one huge file. 5 years later I have started working in company that have over 100 developers and our authorization rules are insanely complex! The product has hundreds of settings and the current user can have many roles and there are some partner specific rules as well. It’s crazy! As we are moving towards to split the application into micro-services in the back-end and in the front-end, this is starting to be a big problem. We currently don’t have any way to share the authorization rules throughout all projects, services, libraries etc. That’s why I came with the idea of implementing Kindergarten in JavaScript, because I hope it will help us to have a clean and consistent way to modularize our application.

How to Use Kindergarten

The core of Kindergarten are modules. We call those modules perimeters, because they usually represents some area in your application (specific page, widget, table, button etc.). Perimeters doesn’t really have to represent a view, they can just act as a container for methods that logically belong together too. Perimeters are included into Sandbox which is another core part of Kindergarten. Perimeter can specify which methods should be exposed into the Sandbox and the rest of the methods will be private (can be used only internally by perimeter). Each perimeter must have a purpose which will act as namespace for all exposed methods. Perimeter has always access to the current user of the application (called the child). Let me show you a simple example of such perimeter.

import { createPerimeter } from 'kindergarten';

// or

// import { Perimeter } from 'kindergarten';



const articlesPerimeter = createPerimeter({

// or

// const articlesPerimeter = new Perimeter({

purpose: 'articles', // a namespace for all exposed methods



govern: {

// everybody can read articles

'can read': () => true,



// only admin or creator of the article might edit it

'can update'(article) {

return this._isAdminOrCreatorOf(article);

},



// User that can update articles can destroy them as well

'can destroy'(article) {

return this.isAllowed('update', article);

}

},



// Those methods will be available through the Sandbox

expose: [

'read',

'update',

'destroy'

],



_isAdminOrCreatorOf(article) {

// Perimeter has always a valid refernce to it's child

return this.child.role === 'admin' || (

this.child.role === 'moderator' &&

this.child === article.author

) && !this.child.isBanned;

},



@guard

read(article, attrs) {

// instead of @guard you can call:

// this.guard('read', article);

return article.read(attrs);

},



@guard

update(article, attrs) {

return article.update(attrs);

},



@guard

destroy(article, attrs) {

return article.destroy(attrs);

}

});



export default articlesPerimeter;

OK, we have a perimeter now. We can now create a simple Sandbox. It’s simple:

import { Sandbox } from 'kindergarten';

import Article from 'wherever/you/store/Article';

import articlesPerimeter from 'wherever/you/store/articlesPerimeter';



class ArticlesController extends Sandbox {

constructor(currentUser) {

super(currentUser);

// This method comes from Sandbox

this.loadModule(articlesPerimeter);

}



read(id) {

const article = Article.find(id);

this.articles.read(article);

}



update(id) {

const article = Article.find(id);

this.articles.update(article);

}



destroy(id) {

const article = Article.find(id);

this.articles.destroy(article);

}

}



export default ArticlesController;

We have created an articles controller that inherits from the Sandbox. You don’t necessary have to inherit from the Sandbox, but use it standalone instead. For example like this:

import { createSandbox } from 'kindergarten';

import Article from 'wherever/you/store/Article';

import articlesPerimeter from 'wherever/you/store/articlesPerimeter';



class ArticlesController extends Sandbox {

constructor(currentUser) {

this.sandbox = createSandbox(currentUser, {

perimeters: [

articlesPerimeter

]

});

}



read(id) {

const article = Article.find(id);

this.sandbox.articles.read(article);

}



update(id, attrs) {

const article = Article.find(id);

this.sandbox.articles.update(article, attrs);

}



destroy(id) {

const article = Article.find(id);

this.sandbox.articles.destroy(article);

}

}



export default ArticlesController;

It really depends on you if you prefer OOP over functional programming or vice versa. Let me show you what we can do with the articles controller:

import ArticlesController from 'wherever/you/store/ArticlesController';



const normalUser = {

role: 'normal-user'

};



const articlesController = new ArticlesController(

normalUser

);



articlesController.read(1); // shows the content of the article

articlesController.update(1); // throws AccessDenied error



const admin = {

role: 'admin'

};



const adminArticlesController = new ArticlesController(

admin

);



articlesController.update(1, {

title: 'My New Super-Awesome Title'

}); // no problemo articlesController.articles.isAllowed(

'destroy',

Article.find(1)

); // true

Kindergarten Can Do a Lot More than That

I have shown you just a very simple example of possible usage of Kindergarten, but you can do a lot more than that! The Sandbox of the Kindergarten is protected by a governess: she is the one responsible for all rules to be respected on the Sandbox. Sandbox is using HeadGoverness by default, but there more governesses available in Kindergarten (see: https://github.com/JiriChara/kindergarten/tree/master/src/kindergarten/governesses). There are also alternative ways to define a rules on the perimeter and much more. If you are interested to learn more please read the README or check the docs. Happy coding!