Vue router permission based on User Role from Django Rest Framework - django

I have Django and Vue project and I need to add permissions in the Vue router based on user role.
I managed to do this in the template by accessing the user data from my user API.
<li class="nav-item active mx-1 mb-1">
<router-link
v-if="user_data.role != 2"
:to="{ name: 'activities' }"
class="btn btn-sm btn-success"
>Activities
</router-link>
</li>
<script>
import { apiService } from "#/common/api.service.js";
export default {
name: "NavbarComponent",
data() {
return {
user_data: {},
}
},
methods: {
setRequestUser() {
this.requestUser = window.localStorage.getItem("username");
},
getUserData() {
// get the details of a question instance from the REST API and call setPageTitle
let endpoint = `/api/user/`;
apiService(endpoint)
.then(data => {
this.user_data = data;
})
},
},
computed: {
isQuestionAuthor() {
return this.requestUser === 'admin_user';
},
isUser() {
return this.requestUser;
},
},
created() {
this.setRequestUser()
this.getUserData()
}
};
</script>
The user doesn't see the button but can still access the pages if enter the path directly in URL.
I can't find a workaround to get the same user data from user API and use it to manage route permissions based on user.role
My router.js looks like this:
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "home",
component: Home
},
{
path: "/activities",
name: "activities",
component: Activities
},
{
path: "/add-activity/:slug?",
name: "activity-editor",
component: ActivityEditor,
props: true
},
{
path: "/activities/:slug",
name: "activity",
component: Activity,
props: true
},
{
path: "/cto-entries",
name: "cto-entries",
component: CTOEntries,
},
{
path: "/add-cto-entry/:slug?",
name: "cto-editor",
component: CTOeditor,
props: true
},
{
path: "/question/:slug",
name: "question",
component: Question,
props: true
},
{
path: "/ask/:slug?",
name: "question-editor",
component: QuestionEditor,
props: true
},
{
path: "/answer/:id",
name: "answer-editor",
component: AnswerEditor,
props: true
},
{
path: "*",
name: "page-not-found",
component: NotFound
}
];
const router = new VueRouter({
mode: "history",
//base: process.env.BASE_URL,
routes
});
export default router;
Is there any way to do this in vue router or there is a better way?
I am new to Vue.js, please help :)

Solved this by using beforeEnter and fetching user role from API...
import { apiService } from "#/common/api.service.js";
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "home",
component: Home
},
{
path: "/activities",
name: "activities",
component: Activities,
beforeEnter(to,from,next) {
var user_data = {};
let endpoint = `/api/user/`;
apiService(endpoint)
.then(data => {
user_data = data;
if(user_data.role!=2){
next();
} else {
next("/");
}
});
},
},

Related

Loopback 4 REST Controller path returns NotFoundError 404

Created a REST Controller with CRUD functions object via the CLI using
lb4 controller media
pointing to an existing MediaRepository for an existing Entity Media model
both of which were generated using the lb4 CLI as well.
A MediaController class was created with all of the REST routes for /media*
The /ping route works fine so I looked for any special routing configuration for it to see if there might be a config messing for /media but nothing was obvious.
An HTTP Get request to /media response with a web page having the following content:
<h1>NotFoundError</h1>
<h2><em>404</em> Endpoint "GET /media" not found.</h2>
There is probably some fundamental configuration or setup that needs to happen but I am just not seeing it.
MediaController class
import {
Count,
CountSchema,
Filter,
repository,
Where,
} from '#loopback/repository';
import {
post,
param,
get,
getFilterSchemaFor,
getWhereSchemaFor,
patch,
put,
del,
requestBody, Request, RestBindings, ResponseObject
} from '#loopback/rest';
import { Media } from '../models';
import { MediaRepository } from '../repositories';
export class MediaController {
constructor(
#repository(MediaRepository)
public mediaRepository: MediaRepository,
) { }
#post('/media', {
responses: {
'200': {
description: 'Media model instance',
content: { 'application/json': { schema: { 'x-ts-type': Media } } },
},
},
})
async create(#requestBody() media: Media): Promise<Media> {
return await this.mediaRepository.create(media);
}
#get('/media/count', {
responses: {
'200': {
description: 'Media model count',
content: { 'application/json': { schema: CountSchema } },
},
},
})
async count(
#param.query.object('where', getWhereSchemaFor(Media)) where?: Where<Media>,
): Promise<Count> {
return await this.mediaRepository.count();
}
#get('/media', {
responses: {
'200': {
description: 'Array of Media model instances',
content: {
'application/json': {
schema: { type: 'array', items: { 'x-ts-type': Media } },
},
},
},
},
})
async find(
#param.query.object('filter', getFilterSchemaFor(Media)) filter?: Filter<Media>,
): Promise<Media[]> {
return await this.mediaRepository.find(filter);
}
#patch('/media', {
responses: {
'200': {
description: 'Media PATCH success count',
content: { 'application/json': { schema: CountSchema } },
},
},
})
async updateAll(
#requestBody() media: Media,
#param.query.object('where', getWhereSchemaFor(Media)) where?: Where<Media>,
): Promise<Count> {
return await this.mediaRepository.updateAll(media, where);
}
#get('/media/{id}', {
responses: {
'200': {
description: 'Media model instance',
content: { 'application/json': { schema: { 'x-ts-type': Media } } },
},
},
})
async findById(#param.path.string('id') id: string): Promise<Media> {
return await this.mediaRepository.findById(id);
}
#patch('/media/{id}', {
responses: {
'204': {
description: 'Media PATCH success',
},
},
})
async updateById(
#param.path.string('id') id: string,
#requestBody() media: Media,
): Promise<void> {
await this.mediaRepository.updateById(id, media);
}
#put('/media/{id}', {
responses: {
'204': {
description: 'Media PUT success',
},
},
})
async replaceById(
#param.path.string('id') id: string,
#requestBody() media: Media,
): Promise<void> {
await this.mediaRepository.replaceById(id, media);
}
#del('/media/{id}', {
responses: {
'204': {
description: 'Media DELETE success',
},
},
})
async deleteById(#param.path.string('id') id: string): Promise<void> {
await this.mediaRepository.deleteById(id);
}
}
So I set lb4 aside for a while while I evaluated other frameworks.
Came back to my lb4 demo project today. No changes to anything since then. Started the application.
npm run start
Browsed to localhost:3000/media
To my surprise it returned a json response. Now my response array was empty and it should have returned something as there were documents in the mongodb datasource but that is a separate issue to figure out.

Accessing model properties in Controller - Ember

So, I'm trying to access my model properties in controller.
Controller:
dashobards: [
{ id: 12, name: 'test' },
{ id: 17, name: 'test2' },
];
In route I have model named dashboards
return Ember.RSVP.hash({
dashboards: this.store.findAll('dashboard'),
}).then((hash) => {
return Ember.RSVP.hash({
dashboards: hash.dashboards
});
}, self);
I wanna have result in controller like this:
dashboards: [
{ id: 12, name: 'test' },
{ id: 17, name: 'test2' },
{ id: 17, name: 'test1' },
{ id: 20, name: 'test20' },
];
In controller I am trying to access this model like this:
this.dashborads = this.get(model.dashobards)
And it's not working, is there any other way of doing that?
Another update How to access complex object which we get it from server in ember data model attibute,
Created twiddle to demonstrate
define attribute with DS.attr(),
export default Model.extend({
permissions:DS.attr()
});
route file,
model(){
return this.store.findAll('dashboard');
}
Your server response should be like,
data: [{
type: 'dashboard',
id: 1,
attributes: {
permissions: {'name':'role1','desc':'description'}
}
}]
hbs file,
{{#each model as |row| }}
Name: {{row.permissions.name}} <br/>
Desc: {{row.permissions.desc}} <br />
{{/each}}
Update:
Still I am not sure about the requirement, Your twiddle should be minimalized working twiddle for better understanding..anyway I will provide my observation,
1.
model(params) {
this.set('id', params.userID);
const self = this;
return Ember.RSVP.hash({
dashboards: this.store.findAll('dashboard'),
user: this.store.findRecord('user', params.userID)
}).then((hash) => {
return Ember.RSVP.hash({
user: hash.user,
dashboards: hash.dashboards
});
}, self);
}
The above code can be simply written like
model(params) {
this.set('id', params.userID);
return Ember.RSVP.hash({
dashboards: this.store.findAll('dashboard'),
user: this.store.findRecord('user', params.userID)
});
}
Its good to always initialize array properties inside init method. refer https://guides.emberjs.com/v2.13.0/object-model/classes-and-instances/
For removing entry from array,
this.dashboard.pushObject({ 'identifier': '', 'role': '' }); try this this.get('dashboard').pushObject({ 'identifier': '', 'role': '' });.
if possible instead of plain object you can use Ember.Object like
this.get('dashboard').pushObject(Ember.Object.create({ 'identifier': '', 'role': '' }));
For removing entry.
removeDashboard(i) {
let dashboard = Ember.get(this, 'dashboard');
Ember.set(this, 'dashboard', dashboard.removeObject(dashboard[i]));
}
The above code can be written like, since i is an index
removeDashboard(i) {
this.get('dashboard').removeAt(i)
}
Just do return this.store.findAll('dashboard'); in route model hook, and dont override setupController hook, then in hbs you should be able to access model that will represent RecordArray. you can have a look at this answer for how to work with this.

How to use one template in multiple Ironrouter route?

I want to display the template named "home" with the route "/" and "/home" but with my code it doesn't work
/** Iron router config file **/
Router.configure({
layoutTemplate: 'layout',
notFoundTemplate: '404',
loadingTemplate: 'loading',
fastRender: true,
});
// Home
Router.route('/', {
name: 'home',
waitOn: function() {
return [
Meteor.subscribe('infosContainers'),
Meteor.subscribe('infosMachines'),
Meteor.subscribe('alertes'),
];
},
fastRender: true,
});
Router.route('/home', {
name: 'home',
waitOn: function() {
return [
Meteor.subscribe('infosContainers'),
Meteor.subscribe('infosMachines'),
Meteor.subscribe('alertes'),
];
},
fastRender: true,
});
It doesn't like the fact that the template "home" is in 2 routes (because if I set name: sokasok in the second one it works )
Could you help me ?
'name' is not for the template render, it's the name of the route. What you need to do is call this.render('home') in action of the route.
Router.route('/home', {
waitOn: function() {
return [
Meteor.subscribe('infosContainers'),
Meteor.subscribe('infosMachines'),
Meteor.subscribe('alertes'),
];
},
action: function(){
this.render('home');
}
fastRender: true,
});

Angular2 dynamic templating

How do I use nested templates in angular2. So basically I have a base component and it's childs
- entry.component.ts
- entry.component.html
- navigation
-- sidenav.ts
-- sidenav.html
-route1
--route1.ts
--route1.html
entry.component.html should be a skeleton and the content should be generated dynamically on route changes.
Is it possible to achieve this?
Use templates with child routes.
I separate mine in public and secure routing everything through the layout then loading the routes for which ever layout is chosen.
Make sure to export the child routes and that the correct routes are called in the layout route. Also make sure you use redirectTo in each child routes file.
app.routing.ts
const APP_ROUTES: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full', },
{ path: '', component: PublicComponent, data: { title: 'Public Views' }, children: PUBLIC_ROUTES },
{ path: '', component: SecureComponent, canActivate: [Guard], data: { title: 'Secure Views' }, children: SECURE_ROUTES }
];
Then I have a layouts folder
layouts
layouts/public/public.components.ts
layouts/public/public.components.html
layouts/secure/secure.components.ts
layouts/secure/secure.components.html
child routes
/public/piublic.routes.ts
export const PUBLIC_ROUTES: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'p404', component: p404Component },
{ path: 'e500', component: e500Component },
{ path: 'login', component: LoginComponent },
{ path: 'register', component: RegisterComponent },
{ path: 'home', component: HomeComponent }
];
/secure/secure.routes.ts
export const SECURE_ROUTES: Routes = [
{ path: '', redirectTo: 'overview', pathMatch: 'full' },
{ path: 'items', component: ItemsComponent },
{ path: 'overview', component: OverviewComponent },
{ path: 'profile', component: ProfileComponent },
{ path: 'reports', component: ReportsComponent }
];
Creating components to load into the template
import { Component, OnInit } from '#angular/core';
#Component({
templateUrl: './benefits.component.html',
})
export class BenefitsComponent {
constructor() { }
}
The create its html template,
/public/benefits.component.html'
Now that it is part of the public route. When someone goes to a public route it will be loaded in the chile routes. so inside your
/public/public.routes.ts file you would add it for every new route you wanted to create to load your new html page.

get from model and then set a new property on it

I have a component:
App.MyChildComponent = Ember.Component.extend({
addTooltips: Ember.on('didInsertElement', function() {
var me = this;
var metrics = this.get('option.metrics');
metrics.forEach(function(e, i) {
me.get('option.metrics').objectAt(i - 1).set('tooltipDisabled', true);
});
});
})
Which is generated inside an each loop by a different component:
App.MyParentComponent = Ember.Component.extend({
...
})
And the template of MyParentComponent is:
{{#each option in options}}
{{my-child option=option}}
{{/each}}
All this, is called by a view with a template like this:
{{my-parent options=options}}
options is defined in the model of the view with:
App.MyViewModel = Ember.Object.extend({
options: Ember.A([
{ metrics: Ember.A([
{ name: 'a' },
{ name: 'b' },
{ name: 'c' }
]) },
{ metrics: Ember.A([
{ name: 'd' },
{ name: 'e' },
{ name: 'f' }
]) },
{ metrics: Ember.A([
{ name: 'g' },
{ name: 'h' },
{ name: 'i' }
]) }
]),
});
When I run me.get('option.metrics').objectAt(i - 1).set('tooltipDisabled', true); I get:
Uncaught TypeError: me.get(...).objectAt(...).set is not a function
What am I doing wrong?
Vanilla JavaScript objects don't have set methods. Use Ember.Objects instead:
App.MyViewModel = Ember.Object.extend({
options: Ember.A([
{ metrics: Ember.A([
Ember.Object.create({ name: 'a' }),
// ...
]) }
]),
});
Demo.