Admin Panel - Anniegavr/Collab-Buddy GitHub Wiki

Aspect

image

The Admin Panel is protected with an extra authentication: image

Modelling the user

Here is how the User as a model is created in the frontend, as User.vue:

<template>
  
</template>

<script>
export default {
  name: "User",
}
export interface User {
  id: number;
  name: string;
  email: string;
  role: string;
}

export class UserClass implements User {
  id: number;
  name: string;
  email: string;
  role: string;

  constructor(id: number, name: string, email: string, role: string) {
    this.id = id;
    this.name = name;
    this.email = email;
    this.role = role;
  }

  isAdmin(): boolean {
    return this.role === 'admin';
  }
}

</script>

<style scoped>

</style>`

Verifying if the user is an admin

To restrict access to the admin panel only for users with admin privileges right in the frontend, I used Vue Router's navigation guards in my router:

router.ts
// some router initialization here
// and then
// authentication guard function
const isAdmin = () => {
    const user = isAuthenticated(); // get the authenticated user
    return user && user.isAdmin; // check if the user has admin privileges
}

// navigation guard to restrict access to admin panel
router.beforeEach((to, from, next) => {
    if (to.matched.some(record => record.meta.requiresAdmin)) {
        if (!isAdmin()) {
            next({ path: '/login' }); // redirect to login page if not authorized
        } else {
            next();
        }
    } else {
        next();
    }
});

Routes that are guarded

This is my router's list that of the frontend endpoints:

    {
        path: '/',
        component: import('./components/MainPage.vue'),
    },
    {
        path: '/home',
        component: () => import('./components/MainPage.vue'),
    },
    {
        path: '/login',
        component: () => import('./components/LoginPage.vue'),
    },
    {
        path: '/admin',
        component: () => import('./components/AdminPanel.vue')
    },
    {
        path: '/admin_proxy',
        component: () => import('./components/AdminAuthPopup.vue')
    },
    {
        path: '/user',
        component: () => import('./models/User.vue')
    },
    {
        path: '/users',
        component: () => import('./components/UsersPage.vue')
    },
    {
        path: '/signup',
        component: () => import('./components/SignUpPage.vue'),
    },
    {
        path: '/admin/all_courses',
        component: () => import('./components/AllCourses.vue'),
    },
    {
        path: '/admin/all_students',
        component: () => import('./components/AllStudents.vue'),
    },
    {
        path: '/teacher/release_assignment',
        component: () => import('./components/ReleaseAssignment.vue'),
    },
    {
        path: '/course_overview',
        component: () => import('./components/CourseOverview.vue'),
    },
    {
        path: '/profile',
        component: () => import('./components/ProfilePage.vue'),
    },
    {
        path: '/admin/assignment_types',
        component: () => import('./models/Assignment.vue')
    },
    {
        path: '/menu',
        component: () => import('./components/SideBarMenu.vue'),
    },
    {
        path: '/admin/add_groups',
        component: () => import('./components/AddGroup.vue'),
        meta: { requiresAdmin: true } // add meta field to mark the route as requiring admin privileges
    },
    {
        path: '/admin/add_student',
        component: () => import('./components/AddStudentsPage.vue'),
        meta: { requiresAdmin: true } // add meta field to mark the route as requiring admin privileges
    },
    {
        path: '/admin/skill_types',
        component: () => import('./components/SkillTypesConfigPage.vue'),
        meta: { requiresAdmin: true } // add meta field to mark the route as requiring admin privileges
    },
    {
        path: '/admin/assignment_progress',
        component: () => import('./components/AssignmentsProgress.vue'),
        meta: { requiresAdmin: true } // add meta field to mark the route as requiring admin privileges
    },
    {
        path: '/teachers',
        component: () => import('./components/TeachersPage.vue')
    },
    {
        path: '/admin/negotiations',
        component: () => import('./components/NegotiationsPage.vue'),
        meta: { requiresAdmin: true } // add meta field to mark the route as requiring admin privileges
    },
    {
        path: '/admin/schedule_config',
        component: () => import('./components/StudentScheduleConfigPage.vue'),
        meta: { requiresAdmin: true } // add meta field to mark the route as requiring admin privileges
    },
    {
        path: '/admin/ll_students',
        component: () => import('./components/AllStudents.vue'),
        meta: { requiresAdmin: true } // add meta field to mark the route as requiring admin privileges
    },
]

It is the meta: { requiresAdmin: true } that does the trick, by double-checking who the user is. So, if I am not authenticated as an admin, the request is forwarded to the login page.

Allow only certain IP addresses

To do so, I firstly created a configuration to record the blacklisted and the whitelisted IP addresses:

export const ipFilterConfig = {
    whitelist: ['127.0.0.1', '::1'], // example whitelist, replace with your own list
    mode: 'allow'
};

export class allowedIps {
    static includes(ipAddress: any) {
        return false;
    }
}

This list shall be iterated through by the same meta guards of the router when checking the request origin and block IP addresses that should not be allowed through.

Total number of requests that the app has received per day or hour

I created a component that keeps track of these requests:

<template>
  <div>
    <h2>Total Requests</h2>
    <div>{{ totalRequests }}</div>
  </div>
</template>

<script>
export default {
  name: 'TotalRequests',
  data() {
    return {
      totalRequests: 0,
    }
  },
  created() {
    // make an API call to get the total number of requests
    // replace the API endpoint with your own
    axios.get('/api/requests/total')
        .then(response => {
          this.totalRequests = response.data;
        })
        .catch(error => {
          console.error(error);
        });
  },
};
</script>

The requests' total shall be saved in the database and the value shall be reset everyday.

⚠️ **GitHub.com Fallback** ⚠️