From e186fd0fac1461f3990db9730ec66bd19cf82e87 Mon Sep 17 00:00:00 2001 From: Aaron Kitzmiller Date: Thu, 26 Sep 2024 15:28:18 -0400 Subject: [PATCH] more updates --- docker-compose.yml | 2 - frontend/src/API/urls.js | 4 ++ frontend/src/main.js | 127 +++++++++++++++++++++++++---------- frontend/src/router/index.js | 2 +- project_name/permissions.py | 7 +- project_name/serializers.py | 2 +- project_name/settings.py | 2 + project_name/urls.py | 2 - project_name/views.py | 12 ++-- 9 files changed, 109 insertions(+), 51 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ccd1efd..912092e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3' - services: testsmtp: image: mailhog/mailhog diff --git a/frontend/src/API/urls.js b/frontend/src/API/urls.js index f476c52..9ef9e33 100644 --- a/frontend/src/API/urls.js +++ b/frontend/src/API/urls.js @@ -32,7 +32,11 @@ export const GET_REQUEST_LIST = 'requests/get-request-list/' export const UPDATE_ONBOARD_REQUESTS = 'onboard-requests/' export const GET_VALID_PROCESSOR_STATES = 'requests/get-valid-processor-states/' export const PRODUCT_USAGES = 'product-usages/' +export const FACILITIES = 'facilities/' export const CALCULATE_BILLING_MONTH = 'billing/calculate-billing-month/' export const BILLING_RECORD_LIST = 'billing/get-billing-record-list/' export const BILLING_RECORDS = 'billing-records/' export const BILLING_RECORD_REVIEW_NOTIFICATION = 'billing/billing-record-review-notification/' +export const REPORT_RUNS = 'report-runs/' +export const REPORTS = 'reports/' +export const RUN_REPORT = 'run-report/' diff --git a/frontend/src/main.js b/frontend/src/main.js index ca207aa..e740582 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -1,20 +1,25 @@ -/* eslint-disable brace-style, no-else-return, import/no-unresolved */ +// The Vue build version to load with the `import` command +// (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import APIService from '@/API/API' import ifxvue from 'ifxvue' -import { IFXRequestAPI } from '@/API/IFXRequestAPI' import APIStore from '@/API/APIStore' import vuexStore from '@/store' -import App from '@/App' -import { router, routes } from '@/router' -import vuetify from '@/plugins/vuetify'; -import 'material-design-icons-iconfont/dist/material-design-icons.css' +import { IFXRequestAPI } from '@/API/IFXRequestAPI' +import vuetify from '@/plugins/vuetify' +import JsonCSV from 'vue-json-csv' +import axios from 'axios' +import VueCookie from 'vue-cookie' +import moment from 'moment' +import { router } from './router' +import App from './App' import 'vuetify/dist/vuetify.min.css' import 'ifxvue/dist/ifxvue.css' import '@mdi/font/css/materialdesignicons.css' -import JsonCSV from 'vue-json-csv' Vue.component('downloadCsv', JsonCSV) +Vue.use(VueCookie) +Vue.config.productionTip = false Vue.config.productionTip = false // Register ifxvue module @@ -26,50 +31,102 @@ const api = new APIService(APIStore) Vue.prototype.$api = Vue.observable(api) api.auth.initAuthUser() -// api.loadUserFromStorage() +// Loop through routes, set options for all paths and admin routes +// To make route admin only, go to router index and add isCNSAdminRoute:true to specific route + +router.beforeEach((to, from, next) => { + if (to.name === 'Forbidden' || to.name === 'Login') { + next() + } + if (api.authUser && api.authUser.isAuthenticated) { + if (to.matched.some((mroute) => mroute.meta.AdminRoute)) { + if (api.authUser.hasGroup('Admin')) { + next() + } else { + next({ name: 'Forbidden' }) + } + } else { + next() + } + } else { + // When a user is not authenticated, this branch will be run twice for some reason + // The query parameter "next" is added the first time, and then just passed along the second time + const routeData = { name: 'Login' } + if (Object.keys(to.query).length !== 0) { + routeData.query = to.query + } else { + routeData.query = { next: to.path } + } + next(routeData) + } +}) const requestApi = new IFXRequestAPI(api, 'default-approver') Vue.prototype.$requestApi = requestApi -// Loop through routes, set options for all paths and admin routes -// To make route admin only, go to router index and add isAdminRoute:true to specific route -// route.pathToRegexpOptions = { strict: true } -const check = (to, from, next) => { - if (api.auth.isAdmin) { - // TODO: add message - next() - } else { - next({ name: 'Forbidden' }) +Vue.filter('users', (value) => { + // Display one or more users via full_name attribute + let names = [] + if (value) { + if (Array.isArray(value)) { + names = value.map((u) => { + return u.full_name ? u.full_name : u.username + }) + } else { + names = [value.full_name ? value.full_name : value.username] + } } -} + return names.join(', ') +}) + +Vue.filter('yesno', (value) => { + return value ? 'Yes' : 'No' +}) -routes.forEach((route) => { - // eslint-disable-next-line no-param-reassign - route.pathToRegexpOptions = { strict: true } - if (route.isAdminRoute) { - // eslint-disable-next-line no-param-reassign - route.beforeEnter = check; +Vue.filter('humanDate', (value) => { + let datestr = '' + if (value) { + datestr = moment(String(value)).format('M/DD/YYYY') } - router.addRoute(route) + return datestr }) -// Disable routes by unathenticated users -router.beforeEach((to, from, next) => { - if (to.name !== 'Home' && !api.auth.isAuthenticated) { - api.auth.login() - .then(() => next()) - .catch(() => next({ name: 'Home' })) - } else { - next() +Vue.filter('timeOnly', (value) => { + let datestr = '' + if (value) { + datestr = moment(String(value)).format('h:mm A') } + return datestr +}) + +Vue.filter('spelledOutDay', (value) => { + let datestr = '' + if (value) { + datestr = moment(String(value)).format('dddd, MMMM Do') + } + return datestr +}) + +// An eventhub is needed to emit and register events in sibling components instantaneously +// Instantiate and add as global mixin +const eventHub = new Vue() +Vue.mixin({ + data: function () { + return { + eventHub: eventHub, + } + }, }) /* eslint-disable no-new */ new Vue({ - vuetify, el: '#app', + vuetify, store: vuexStore, router, + axios, components: { App }, - render: h => h(App) + render: (h) => h(App), }) + +// auth.checkAuthentication() diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 44054bf..8456ff3 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -228,7 +228,7 @@ const routes = [ }, { path: '/accounts/list/', - name: 'IFXAccountList', + name: 'AccountList', component: IFXAccountList }, { diff --git a/project_name/permissions.py b/project_name/permissions.py index 27def53..07a460f 100644 --- a/project_name/permissions.py +++ b/project_name/permissions.py @@ -8,6 +8,7 @@ import json import logging +from django.conf import settings from rest_framework import permissions from ifxuser.permissions import UserViewSetPermissions from ifxuser import roles as Roles @@ -19,7 +20,7 @@ class AdminPermission(permissions.IsAuthenticated): User must be an admin ''' def has_permission(self, request, view): - result = Roles.has_role(settings.GROUPS.ADMIN_GROUP_NAME, request.user) + result = Roles.has_role(settings.ROLES.ADMIN_ROLE_NAME, request.user) logger.debug('user is admin? %s', str(result)) return result @@ -39,7 +40,7 @@ def has_permission(self, request, view): username = data.get('username') except json.JSONDecodeError: pass - return (Roles.has_role(settings.GROUPS.ADMIN_GROUP_NAME, request.user) or request.user.username == username) and super().has_permission(request, view) + return (Roles.has_role(settings.ROLES.ADMIN_ROLE_NAME, request.user) or request.user.username == username) and super().has_permission(request, view) class {{project_name|title}}UserViewSetPermissions(UserViewSetPermissions): @@ -50,4 +51,4 @@ def user_is_admin(self, user): ''' How admin is determined ''' - return Roles.has_role(settings.GROUPS.ADMIN_GROUP_NAME, request.user) + return Roles.has_role(settings.ROLES.ADMIN_ROLE_NAME, request.user) diff --git a/project_name/serializers.py b/project_name/serializers.py index 9d7d4c8..d873432 100644 --- a/project_name/serializers.py +++ b/project_name/serializers.py @@ -164,7 +164,7 @@ class UserViewSet(viewsets.ModelViewSet): permission_classes = [permissions.{{project_name|title}}UserViewSetPermissions] def get_queryset(self): - if Roles.has_role(settings.GROUPS.ADMIN_GROUP_NAME, self.request.user): + if Roles.has_role(settings.ROLES.ADMIN_ROLE_NAME, self.request.user): groupstr = self.request.query_params.get('groups') username = self.request.query_params.get('username') search = self.request.query_params.get('search') diff --git a/project_name/settings.py b/project_name/settings.py index e9211a9..81cce07 100644 --- a/project_name/settings.py +++ b/project_name/settings.py @@ -13,6 +13,7 @@ """ import os +from decimal import Decimal # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -336,6 +337,7 @@ class ROLES(): '{{project_name}}_user', ] PACKAGE_NAME = '{{project_name}}.roles' + ADMIN_ROLE_NAME = '{{project_name}}_admin' ACCOUNT_REQUEST_TRACKS = ['{{project_name}}_admin', '{{project_name}}_user'] diff --git a/project_name/urls.py b/project_name/urls.py index 33d1a81..6db91c8 100644 --- a/project_name/urls.py +++ b/project_name/urls.py @@ -32,7 +32,6 @@ router.register(r'billing-records', ifxbilling_serializers.BillingRecordViewSet, 'billing-records') router.register(r'products', ifxbilling_serializers.ProductViewSet, 'products') router.register(r'facilities', ifxbilling_serializers.FacilityViewSet, 'facilities') -router.register(r'accounts', ifxbilling_serializers.AccountViewSet, 'account') router.register(r'report-runs', ReportRunViewSet, 'report-run') router.register(r'reports', ReportViewSet, 'report') @@ -45,7 +44,6 @@ path(r'{{project_name}}/api/users/get-nanite-login/', views.get_ifxapp_nanite_login), path(r'{{project_name}}/api/onboard-requests//', request_views.onboard_requests), path(r'{{project_name}}/api/onboard-requests/', request_views.onboard_requests), - path(r'{{project_name}}/api/users/update-person/', views.update_person), path(r'{{project_name}}/api/requests/get-request-list/', request_views.get_request_list), path(r'{{project_name}}/api/requests/get-valid-processor-states/', request_views.get_valid_processor_states), path(r'{{project_name}}/api/requests/set-request-state/', request_views.set_request_state), diff --git a/project_name/views.py b/project_name/views.py index 738ac9e..5b9b454 100644 --- a/project_name/views.py +++ b/project_name/views.py @@ -24,9 +24,7 @@ from ifxmail.client.views import messages, readUpdateOrDeleteMessage, mailings, readMailing from ifxuser.models import USER_EDITABLE_PERSON_FIELDS, ADMIN_EDITABLE_PERSON_FIELDS from ifxuser.nanites import updateOrCreateIfxUser -from ifxuser.views import get_ifxapp_nanite_login as ifxuser_get_ifxapp_nanite_login -from ifxuser.views import get_location_info as ifxuser_get_location_info -from ifxuser.views import get_contact_list as ifxuser_get_contact_list +from ifxuser import views as ifxuser_views from ifxuser import roles as Roles from nanites.client import API as NanitesAPI from {{project_name}}.permissions import AdminOrOwner, AdminPermission @@ -137,7 +135,7 @@ def get_ifxapp_nanite_login(request): ''' Get a nanite login by username ''' - return ifxuser_get_ifxapp_nanite_login(request) + return ifxuser_views.get_ifxapp_nanite_login(request) @@ -145,7 +143,7 @@ def get_location_info(request): ''' Address location information ''' - return ifxuser_get_location_info(request) + return ifxuser_views.get_location_info(request) @permission_classes((AdminPermission, )) @@ -153,14 +151,14 @@ def get_contact_list(request): ''' Contact list ''' - return ifxuser_get_contact_list(request) + return ifxuser_views.get_contact_list(request) @permission_classes((AdminPermission, )) def get_contactables(request): ''' ifxuser get_contactables with admin permission ''' - return ifxuser_get_contactables(request) + return ifxuser_views.get_contactables(request) @permission_classes((AdminPermission, ))