أبحث عن أسهل طريق لعمل Access Control Listلإطار Vuejs

بالنسبة للـ ACL أو ما يعرف بـ Access Control List
هناك آلاف الطرق التي يمكن أن تستخدم…

طبقا لموقع laravel قمت بعمل Authorization system ونجحت في تخصيص وتقسيم صلاحيات المستخدمين كالتالي:

public function boot()
    {
        $this->registerPolicies();

        Gate::define('isAdmin', function ($user) {
            return $user->type === 'admin';
        });

        Gate::define('isAuthor', function ($user) {
            return $user->type === 'author';
        });

        Gate::define('isUser', function ($user) {
            return $user->type === 'user';
        });

        Passport::routes();
    }
}

حاليا سجلت الدخول بمستخدم عادي user

الآن أحاول في الVuejs أن اقوم بسيناريو شبيه مثل ذلك (عمل class) كالتالي:

export default class Gate {

    constructor(user){
        this.user = user;
        
    }
    
    isAdmin(){
        return this.user.type === 'admin';
    }

    isUser(){
        return this.user.type === 'user';
    }
}

لكن المشكلة لا أستطيع جلب قيمة user (المستخدم الحالي) علما بأنه لا يتم ارسال GET requests حيث قمت باعطاء الصلاحيات وسيتعارض ذلك مع الغرض المطلوب

2 Likes

عندما يحصل العضو على صلاحيات بمجرد تسجيلة للدخول وقتها تستطيع الإستفادة من الـ vuex والذي هو مكتبة للـ state management والذي تستطيع من خلالة جلب ما تريد في اي وقت واي مكان.

2 Likes

كما اشار محمد فقط اعمل الصلاحيات تظهر الواجهات المطلوبة (مثلا المدير لديه واجهة خاصة به)٬ ويتم جلب بيانات المستخدم من ال Vuex

يتبقى ان لا تعتمد على الحماية فقط من الفرونت٬ اي يجب عمل permissions على ال api حسب صلاحيات المستخدم :wink:

1 Like

فاتني أن اقرأ الـ documentation لـ vuex … نعم شكرا لهذا التنبيه!

بالضبط… وبالفعل تم عمل الحماية على الAPI … التحدي الآن هو الفرونت اند… لأنه لازال بإمكان الuser الدخول على صفحة الإدارة عن طريق GET

ما فعلته حتى الآن:
1- قمت بتثبيت Vuex
2- أنشأت ملف js مستقل اسمه: vue-user-module

export default {
    namespaced: true,
    state: {
        name: '',
        email: '',
        password: '',
        type: '',
        bio: '',
        photo: ''
},
    mutations: {
        grab() {
            axios.get('api/profile').then(({data})=>{this.state = data});
        }
    }
}

3- في ملف app.js أنشأت متغير جديد:

import Vuex from 'vuex';
Vue.use(Vuex);

import user from './vue-user-module';
const store = new Vuex.Store({
  modules: {
    user
  }
});

قمت بإضافة المتغير الجديد الى app:

const app = new Vue({
    router,
    store,
    el: '#app',
    mounted() {
      console.log(this.$store.user);
    }
});

يبدو أنها تعمل بشكل جيد… لكن يجب أن أحل تحدي الحصول على المستخدم

1 Like

هل بالإمكان استغلال هذا الأمر بطريقة ما؟

auth
    $current = @json(auth()->user())
@endauth
1 Like

انت اقتربت من الحل كثيراً، لكن بالنسبة لحماية الصفحات فقط ما تحتاج هو الـ navigation guard

https://router.vuejs.org/guide/advanced/navigation-guards.html#global-after-hooks

هذا مثال قام محمد بالعمل عليه

https://github.com/coretabs/dorm-portal/blob/master/src/router.js

لاحظ أن الـ manager ما يقدر يدخل إلا لو كان عنده isAdmin = True (وإلا يتم تحويله على login):

    {
      path: '/manage',
      component: DormManager,
      beforeEnter: (to, from, next) => { (store.getters.isAdmin) ? next() : next('/login') }
    },
1 Like

ممتاز جدا…
الnavigation guards سهلة جدا…
سأتفقد الروابط :slight_smile:

1 Like

وأيضاً لاحظ كيف يتم الاحتفاظ بالبيانات داخل الـ store

أولا هذه أكواد استهلاك backend api بالـ axios عند عمل login

https://github.com/coretabs/dorm-portal/blob/d2583417dd77969ba9ceced489484121bd835aec/src/backend.js#L58

وهنا يتم عمل login بالـ component وحفظها إلى داخل الـ store

https://github.com/coretabs/dorm-portal/blob/6f9de0f79a8c03065b2258d4eb3e7e8b1ac65cdd/src/components/ReservationComponents/Login/Login.js#L32

1 Like

بالنسبة للـ login هو موجود بالباك اند… لكن هل نفس السيناريو يتم في vue أيضا بنفس الطريقة؟
بمعنى أقوم بانشاء api خاص بـ Vue ؟

1 Like

لا تقم بإنشاء api، فقط ربط بالـ axios مثلما هو موضح في الأكواد في آخر تعليق

لكن تحتاج لما تعمل login مباشرة تقوم بالإحتفاظ بالبيانات لداخل الـ store لكي تعمل logic على أساسها (مثلاً يقدر المستخدم يحجز فندق فقط لما هو مسجل دخول).

1 Like

ممتاز جدا …حاليا جربت فكرة أخرى داخل app.js في المتغير الرئيسي: const app = new Vue بإضافة التالي:

router,
    el: '#app',
    data () {
      return {
        user: null
      };
    },
    created () {
      // As app intializes user is fetched and saved
      this.fetchUser();
    },
    methods: {
      fetchUser () {
        axios.get('/api/profile')
          .then(response => {this.user = response.data; console.log(this.user);})
          .catch(error => {
            alert('Something went wrong');
            console.error(error.response.data);
          });
      }
    }

وبالفعل حصلت على بيانات الuser:

1 Like

هذه محاولة… لكن ينقصها شيء ما… (هدفي الأول هو الحصول على users)

  • داخل ملف store هذه محاولتي:
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export const store = new Vuex.Store({
    state: {
        users: []
    },
    actions: {
        loadUsers({commit}) {
            axios.get('api/user').then(({response}) => {
                // console.log('Store js'+ response.data);
                commit('updateData', response.data);
            });
        }
    },
    mutations:{
        updateData(state, users) {
            state.users = users;
        },
    }
});

التعريف داخل app.js:

new Vue({
    router,
    store: store,
    el: '#app',
    created() {
      console.log('test users array: ' + this.$store.state.users)
      this.$store.dispatch('loadUsers').then( users => {
        this.users = users
      }).catch(() => {})
    }
});

النتيجة:


1 Like

تم التعديل… حصلت على الusers

انا مستمتع صراحة!!! :smiley:

1 Like

بحسب فهمي للDocumentation أود أن استورد الusers من الـstore في component خاص…

  • في كومبوننت المستخدمين قمت بكتابة التالي:
// -- data() -- //
        data() {
            return {
                title: 'users management',

                users: [], // for BootstrapVue table
      },
        computed: {
            popUsers(users){
                // this.users = users;
                users = this.$store.dispatch('loadUserData', users);
                console.log(this.users)
                
            }
        },

قمت بصياغة الدالة بشكل اخر:

popUsers(){
                // this.users = users;
                // users = this.$store.dispatch('loadUserData', users);
                this.users = this.$store.getters.users;
                console.log(this.users)
                
            }

لكن كلا من الطريقتين ينتج في الكونسول data object بهذا الشكل:

[__ob__: Observer]
length: 0
__ob__: Observer {value: Array(0), dep: Dep, vmCount: 0}
__proto__: Array

لم أجد في الdocumentation استدلال أو مثال يوضح كيفية نسخ الarray بشكل صحيح

1 Like

ليه انت عامل array ؟ هو يوزر واحد بس اللي يسجل دخول يعني object واحد فقط الذي تحتفظ ببياناته

بمعنى بدلا من [] استعمل {}

1 Like

كنت أفكر في ادخال القيم في array حتى يمكن استخدامه في الجدول مثلا …
لا مشكلة … سأستعمل {} وأرى النتائج…

popUsers(){
                this.users = this.$store.dispatch('loadUsers')
                console.log(this.users)
            }

أحاول أن افهم السلوك… بصراحة نفذت طاقتي!
هذه محاولة أخرى:

getters: {
        names: (state) => (name) => {return state.users.find(e => {return e.name === name})}
    },

in component

popUsers(){
                this.users = this.$store.dispatch('loadUsers').then(()=>{
                    console.log(this.$store.getters.names)
                })

Result

it is printing the function itself!