Частично реализовано взаимодействите с правами пользователя;

Для компоненов PrivateContent, PrivateMenuItem и PrivateRoute можно указывать требуемые права и/или роли и способ их совмещения;
Добавлена ленивая подгрузка страниц для тестирования;
This commit is contained in:
goodmice 2021-12-07 19:45:13 +05:00
parent 741449fb99
commit c6d3814313
6 changed files with 200 additions and 74 deletions

View File

@ -1,28 +1,13 @@
export type Role = string
const admins: Role[] = ['администратор', 'админ', 'admin']
export const isInRole = (roles?: Role[]): boolean => {
// if (localStorage['login'] === 'dev')
// return true
if(!roles?.length)
return true
const role: Role = localStorage['roleName']?.toLowerCase()
if(admins.indexOf(role) > -1)
return true
for(const r of roles)
if(r.toLowerCase() === role)
return true
return false
}
export const privateContent = <T extends unknown>(roles?: Role[], children?: T): T | null | undefined => {
return isInRole(roles) ? children : null
}
import React from 'react'
import { Role, Permissions, hasAccess, PermissionMixingType } from '../../utils/permissions'
type PrivateContentProps = {
permissions?: Permissions
roles?: Role[]
mixing?: PermissionMixingType
children?: any
}
export const PrivateContent: React.FC<PrivateContentProps> = ({ roles, children }) => privateContent(roles, children)
export const PrivateContent: React.FC<PrivateContentProps> = ({ permissions, roles, mixing, children }) => {
return (hasAccess({ permissions, roles, mixing }) && children) || null
}

View File

@ -1,12 +1,15 @@
import React from 'react'
import { Menu } from 'antd'
import { isInRole, Role } from './PrivateContent'
import { hasAccess, Role, Permissions, PermissionMixingType } from '../../utils/permissions'
type PrivateMenuItemProps = {
roles: Role[]
roles?: Role[]
permissions?: Permissions
mixing?: PermissionMixingType
[props: string]: any
}
export const PrivateMenuItem: React.FC<PrivateMenuItemProps> = ({ roles, ...props }) => {
return isInRole(roles) ? <Menu.Item {...props}/> : null
export const PrivateMenuItem: React.FC<PrivateMenuItemProps> = ({ roles, permissions, mixing, ...props }) => {
return hasAccess({ permissions, roles, mixing }) ? <Menu.Item {...props}/> : null
}

View File

@ -1,21 +1,24 @@
import React from 'react'
import { Route, Redirect } from 'react-router-dom'
import { isInRole, Role } from './PrivateContent'
import { Role, Permissions, hasAccess } from '../../utils/permissions'
type PrivateRouteProps = {
children: any
permissions?: Permissions
roles: Role[]
component?: any
children?: any
[other: string]: any
}
export const PrivateRoute: React.FC<PrivateRouteProps> = ({ children, roles, ...other }) => {
const token = localStorage['token']
export const PrivateRoute: React.FC<PrivateRouteProps> = ({ permissions, roles, component, children, ...other }) => {
const available = localStorage['token'] && hasAccess({ permissions, roles })
return (
<Route
{...other}
render={({ location }) => token && isInRole(roles) ? children : (
<Redirect to={{ pathname: "/login", state: { from: location } }} />)
}
<Route {...other}
component={available && component}
render={({ location }) => available ? children : (
<Redirect to={{ pathname: '/login', state: { from: location } }} />
)}
/>
)
}

View File

@ -1,3 +1,3 @@
export { PrivateRoute } from './PrivateRoute'
export { privateContent, PrivateContent } from './PrivateContent'
export { PrivateContent } from './PrivateContent'
export { PrivateMenuItem } from './PrivateMenuItem'

View File

@ -1,11 +1,14 @@
import React, { Suspense } from 'react'
import { Layout, Menu } from 'antd'
import { Switch, Link, useParams, Redirect, Route } from 'react-router-dom'
import { PrivateMenuItem, PrivateRoute } from '../../components/Private'
import { ClusterController } from './ClusterController'
import { CompanyController } from './CompanyController'
import { DepositController } from './DepositController'
import { UserController } from './UserController'
import { WellController } from './WellController'
import { PermissionNames, PermissionValue } from '../../utils/permissions'
const ClusterController = React.lazy(() => import('./ClusterController'))
const CompanyController = React.lazy(() => import('./CompanyController'))
const DepositController = React.lazy(() => import('./DepositController'))
const UserController = React.lazy(() => import('./UserController'))
const WellController = React.lazy(() => import('./WellController'))
export const AdminPanel = () => {
const { tab } = useParams()
@ -18,45 +21,37 @@ export const AdminPanel = () => {
selectable={true}
selectedKeys={[tab]}
>
<PrivateMenuItem roles={['deposit_admin']} key={'deposit'}>
<PrivateMenuItem key={'deposit'} permissions={[[PermissionNames.admin.deposit, PermissionValue.Read]]}>
<Link to={`${rootPath}/deposit`}>Месторождения</Link>
</PrivateMenuItem>
<PrivateMenuItem roles={['cluster_admin']} key={'cluster'}>
<PrivateMenuItem key={'cluster'} permissions={[[PermissionNames.admin.cluster, PermissionValue.Read]]}>
<Link to={`${rootPath}/cluster`}>Кусты</Link>
</PrivateMenuItem>
<PrivateMenuItem roles={['well_admin']} key={'well'}>
<PrivateMenuItem key={'well'} permissions={[[PermissionNames.admin.well, PermissionValue.Read]]}>
<Link to={`${rootPath}/well`}>Скважины</Link>
</PrivateMenuItem>
<PrivateMenuItem roles={['user_admin']} key={'user'}>
<PrivateMenuItem key={'user'} permissions={[[PermissionNames.admin.user, PermissionValue.Read]]}>
<Link to={`${rootPath}/user`}>Пользователи</Link>
</PrivateMenuItem>
<PrivateMenuItem roles={['company_admin']} key={'company'}>
<PrivateMenuItem key={'company'} permissions={[[PermissionNames.admin.company, PermissionValue.Read]]}>
<Link to={`${rootPath}/company`}>Компании</Link>
</PrivateMenuItem>
</Menu>
<Layout>
<Layout.Content className={'site-layout-background'}>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<PrivateRoute roles={['deposit_admin']} path={`${rootPath}/deposit`}>
<DepositController />
</PrivateRoute>
<PrivateRoute roles={['cluster_admin']} path={`${rootPath}/cluster`}>
<ClusterController />
</PrivateRoute>
<PrivateRoute roles={['well_admin']} path={`${rootPath}/well`}>
<WellController />
</PrivateRoute>
<PrivateRoute roles={['user_admin']} path={`${rootPath}/user`}>
<UserController />
</PrivateRoute>
<PrivateRoute roles={['company_admin']} path={`${rootPath}/company`}>
<CompanyController />
</PrivateRoute>
<PrivateRoute roles={['deposit_admin']} path={`${rootPath}/deposit`} component={DepositController} />
<PrivateRoute roles={['cluster_admin']} path={`${rootPath}/cluster`} component={ClusterController} />
<PrivateRoute roles={[ 'well_admin']} path={`${rootPath}/well` } component={ WellController} />
<PrivateRoute roles={[ 'user_admin']} path={`${rootPath}/user` } component={ UserController} />
<PrivateRoute roles={['company_admin']} path={`${rootPath}/company`} component={CompanyController} />
<Route path={'/'}>
<Redirect to={`${rootPath}/deposit`}/>
</Route>
</Switch>
</Suspense>
</Layout.Content>
</Layout>
</Layout>

140
src/utils/permissions.ts Normal file
View File

@ -0,0 +1,140 @@
export type Role = string
export type PermissionName = string
export enum PermissionValue {
Nothing = 0,
Read = 1 << 0,
Write = 1 << 1,
Aprove = 1 << 1,
Update = 1 << 1,
Insert = 1 << 2,
Delete = 1 << 3,
}
export type PermissionMixingType = 'and' | 'or'
export type Permissions = [PermissionName, PermissionValue][]
export const PermissionNames = {
deposit: 'deposit',
cluster: 'cluster',
well: {
_self: 'well',
archive: 'well/archive',
measure: 'well/measure',
message: 'well/message',
report: 'well/report',
drillingProgram: 'well/drillingProgram',
telemetry: {
_self: 'well/telemetry',
status: 'well/telemetry/status',
rop: 'well/telemetry/rop',
},
operations: {
_self: 'well/operations',
tvd: 'well/operations/tvd',
sections: 'well/operations/sections',
plan: 'well/operations/plan',
fact: 'well/operations/fact',
drillProccesFlow: 'well/operations/drillProccesFlow',
params: 'well/operations/params',
composite: {
_self: 'well/operations/composite',
wells: 'well/operations/composite/wells',
sections: 'well/operations/composite/sections',
}
},
document: {
_self: 'well/document',
fluidService: 'well/document/fluidService',
cementing: 'well/document/cementing',
nnb: 'well/document/nnb',
gti: 'well/document/gti',
documentsForWell: 'well/document/documentsForWell',
supervisor: 'well/document/supervisor',
master: 'well/document/master'
},
},
admin: {
_self: 'admin',
deposit: 'admin/deposit',
cluster: 'admin/cluster',
well: 'admin/well',
user: 'admin/user',
company: 'admin/company',
}
}
const admins: Role[] = ['администратор', 'админ', 'admin']
export const hasPermissions = (permissions?: Permissions): boolean => {
if (!permissions?.length)
return true
return true // TODO: Написать проверку на доступ через права
return false
}
export const isInRole = (roles?: Role[]): boolean => {
if (localStorage['login'] === 'dev') return true // TODO: Удалить строку
if (!roles?.length)
return true
// TODO: Переписать проверку на доступ через роли
const role: Role = localStorage['roleName']?.toLowerCase()
if (admins.indexOf(role) > -1)
return true
for (const r of roles)
if (r.toLowerCase() === role)
return true
return false
}
export type hasAccessProps = {
permissions?: Permissions
roles?: Role[]
mixing?: PermissionMixingType
}
export const hasAccess = ({permissions, roles, mixing}: hasAccessProps): boolean => {
switch (mixing) {
case 'or':
return hasPermissions(permissions) || isInRole(roles)
case 'and':
default:
return hasPermissions(permissions) && isInRole(roles)
}
}
/*
deposit
cluster
well (R)
well/archive (R)
well/message (R)
well/report (R)
well/measure (RW)
well/drillingProgram (RU согласовать)
well/telemetry (R)
well/telemetry:status (RW)
well/telemetry:rop (RW)
well/operations (R)
well/operations/tvd (R)
well/operations/sections (R)
well/operations/plan (RW)
well/operations/fact (RW)
well/operations/drillProccesFlow (RW)
well/operations/params (RW)
well/operations/composite (R)
well/operations/composite/wells (R)
well/operations/composite/sections (RW)
well/document (R)
well/document/fluidService (RU)
well/document/cementing (RU)
well/document/nnb (RU)
well/document/gti (RU)
well/document/documentsForWell (RU)
well/document/supervisor (RU)
well/document/master (RU)
admin (R)
admin/deposit (RAED)
admin/cluster (RAED)
admin/well (RAED)
admin/user (RAED)
admin/company (RAED)
*/