December 19, 2018
Me: Man we're stabilising the framework and making it way more extensible than it already is, so honestly, I have no idea.
Well last weekend I was feeling the need of trying to find a pure front-end solution to hide plugins and content types depending on the admin's role so the Strapi user can answer to their clients needs.
The idea behind this article is to provide a quick way to hide some views for a specific user's admin role. However, since this is a front-end hack, I need to state that the back-end won't make any difference between the "sub-roles" set for the administration panel (so it's really not secure) and such logic might need to be applied in both the Strapi's UI and your client's site.
We won't provide any support for bugs related to this article and since the codebase evolves really fast, this logic is really likely to change (it won't be really stable if I make a minor modification into the admin).
Now that everything has been said, let's dig into some practical example!
1️⃣ In a terminal window, clone the repository.
git clone email@example.com:strapi/strapi.git
2️⃣ Then launch the setup.
cd strapi && npm run setup
3️⃣ Open a new terminal window, create a new project and set the desired database.
strapi new blog --dev
4️⃣ Start the server.
cd blog && strapi start
5️⃣ In a new terminal window, launch the front-end server.
cd blog/admin && npm start
6️⃣ Go to localhost:4000/admin.
In this tutorial we will create a blog that contains articles. Each article is linked to several tags and vice versa, one category is linked to several categories. To create these content types we will use the Content-type Builder plugin that is already installed in each generated project.
Go to the Content Type Builder plugin and create the following content-types.
description(text): if you want the Rich Text Feature enabled visit the Advanced Settings Tab and select
Display as WYSIWYG
Then, click on the Save button and the Content Type should appear in the left menu. Repeat the process for the others content-types.
articles(many-to-many relation with
articles(many-to-one relation with
At this point, you should have 4 models displayed in the left menu. BTW, I'm always amazed by how fast Strapi is to create an API.
Now that the blog is created I'm going to explain my approach to hide and block some plugins depending on the admin user's role.
Currently, only admin users can access the administration panel, those admin users have all rights : they can create/delete/modify both Content Types and Data, set up permissions and modify the app's settings.
In order to hide some parts of the administration panel we need to add a field to the existing User Model like
admin_layout that we're going to use to pimp up our UI. When the user will logs in, the front-end will retrieve this "sub role" and depending on it, will remove elements or functionalities from the front-end.
The created "sub role" will only affect the front-end, for the back-end those admin users will still have all the rights so you might need to make the same approach in your client application.
For my blog API I want to create three "sub roles" :
admin, author and editor. To do so, go to the User model in the content-type-builer and add a new enumeration field called
Don't forget to visit to the Advanced Settings Tab, click on
required and save the modifications. Finally, your model should look like:
We have now created a specific field
admin_layoutthat is necessary for the admin to hide elements from the menu.
The only problem now is that the first registered user (you since you started with a fresh project) doesn't have this property so, we're going to modify the
Since we created a
dev project for the sake of the example we need to make sure the first registered user has the
admin_layout "sub role" of type admin for your production one. So we need to set this default role from the front-end of the
With this modification the first registered user will have the
admin sub role.
At this point, we have just created our API and added a new field to the existing User model. We are now going to see how to hide some plugins and then, how to hide Content Types and views depending on the admin user's sub role.
In the first part we're going to tackle the question of hiding/removing some plugins depending on the user's role. Here what we want to do is delete a plugin from the UI only (it will still remain active in the back-end) and we will handle the special case of hiding the Users & Permissions one.
At some point we need to tell the administration which plugins to hide for a specific user "sub role". To do so, we need to create an
adminLayout.js file where we will write the UI's permissions and that the front-end will retrieve. Since this is a pure front-end "hack" the back-end configuration is pretty easy.
1- Create the
2- Send it to the front-end.
This step is the only back-end configuration that we need to do, the rest will only be a front-end hack...
Now that our back-end is ready we need to undertake some complex front-end modifications. The first one is in the
/admin folder. We are going to modify the following elements AdminPage, App, AppLoader, LeftMenuLinkContainer, Logout.
Here we just need to make a minor update in order to tell the
AppLoader container to stop showing a loader if a plugin is deleted from our store. Since there will be a difference between the number of plugins that the front-end should load and the ones that are loaded if we don't update this part the application will show a loader indefinitely.
Before getting any further we have to update the
shouldLoad instance of this container.
In this section we're are going :
adminLayoutproperty in our saga
1️⃣ Create a new constant and action in the AdminPage container
2️⃣ Update our current reducer with a new key
3️⃣ Create a new selector so we can retrieve the
adminLayout property in our saga
4️⃣ Update the saga in order to retrieve the
adminLayout and hide the desired plugins
5️⃣ Modifying the way we load our plugins
At this point you may have noticed that we handle a special case we trying to hide the
users-permissions plugin for a specific role. This is because if you delete this plugin from our main store the authentication won't be applied in our front-end so the user won't be able to make any request to the backend. In order to hide this plugin in the application we're going to update the
LeftMenuLinkContainer component. We already handled the case of trying to access this plugin using the url since it will display the
The idea here is just to remove the link from the menu.
After all this modifications, we're facing a new issue: how to reset all our modification if a user logs out and logs in with a new account? We have to "reset" our layout and the easiest way is to reload our application.
What if we want to hide some Content Types and maybe give access to the list view but prevent an admin user from creating a new entry?
To do so, we need to add new keys to the
adminLayout.js file so the front-end will know which content types to show and which create views to block
Here we need to add two new keys to our
adminLayout.js user's role :
contentTypeToReadOnly, you'll need to make the modifications and restart your Strapi server.
In this part, we will not only hide content types from the left menu but also prevent some view from being accessed by using our
Before digging into some code we need to understand how the content types' links are displayed in the left menu. Here what happens is that when the
content-manager is mounted in our application his
bootstrap.js file is called and it inserts into our main store a
plugin property containing the
leftMenuSections which has all our content types' link. Then, the
LeftMenuLinkContainer "search" this property in all our plugins to display the different links.
Like we did in the first part of this tutorial we will modify the
AdminPage's saga so it updates these keys by removing the desired content types from one plugins's object. To make the front-end a bit more secure not only will we delete the content types from the left menu but also we will make sure a content type can't be accessed (if said so) by typing the url directly into the browser.
To do so, we will create an object
ctmAdminLayout in the
AdminPage's reducer that we're going to pass to the
content-manager using the old React context API.
The admin is using the first context api and I will update it to the last one soon so you might need to make some further modifications.
Here are the elements that we need to modify:
Since we're want to store a new property in the reducer we need to create a new constant, action and add it into the reducer.
1️⃣ Create a new constant:
2️⃣ Create a new action:
3️⃣ Update the reducer:
At this point nothing is happening in our application the User model is still displaying even though we have set it to be hidden in our
adminLayout.js file. Like we did in the part one we are going to develop the logic in the
4️⃣ Update the saga to remove the content types from the left menu:
Finally we need to pass the
ctmAdminLayout object to the
5️⃣ Update the index.js
The code from above will only remove the corresponding content types from the left menu. We now need to modify the content manager to make the appropriate views unaccessible by setting directly the URL in the browser.
As we did with the
users-permissions plugin we are going to use the
BlockerComponent to prevent a user from accessing a view that he can't.
As you may have seen I have introduced a
contentTypestoReadOnly key in the
adminLayout.js file it allows a user to just display the data in the
ListPage container. So if this condition is met, we need to hide the create button first and then block the view if he tries to access a content type by URL.
In this part we only to update two containers in the
EditPagewith the same logic:
1️⃣ Update the ListPage container:
2️⃣ Update the EditPage container:
Well, we did a lot of modifications but I think that you can extend this logic to fit your needs. As a reminder the code might need to be updated with the releases but it will be a temporary solution until we release the
Admin & Permissions plugin.