Исправлены методы работы с правами и хранилищем

Добавлено описание сайта
This commit is contained in:
Александр Сироткин 2021-12-29 17:48:10 +05:00
parent 37889cb6ce
commit d27d3a1632
15 changed files with 64 additions and 41 deletions

View File

@ -7,7 +7,7 @@
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta <meta
name="description" name="description"
content="Web site created using create-react-app" content="онлайн мониторинг процесса бурения в реальном времени в офисе заказчика"
/> />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>АСБ Vision</title> <title>АСБ Vision</title>
@ -17,3 +17,4 @@
<div id="root"></div> <div id="root"></div>
</body> </body>
</html> </html>
ц

View File

@ -12,7 +12,7 @@ import { OpenAPI } from './services/api'
import { PrivateRoute } from './components/Private' import { PrivateRoute } from './components/Private'
//OpenAPI.BASE = 'http://localhost:3000' //OpenAPI.BASE = 'http://localhost:3000'
OpenAPI.TOKEN = localStorage['token'] OpenAPI.TOKEN = localStorage.getItem('token')
OpenAPI.HEADERS = {'Content-Type': 'application/json'} OpenAPI.HEADERS = {'Content-Type': 'application/json'}
export default function App() { export default function App() {

View File

@ -8,6 +8,7 @@ import { passwordRules, createPasswordRules } from '../utils/validationRules'
import LoaderPortal from './LoaderPortal' import LoaderPortal from './LoaderPortal'
import { invokeWebApiWrapperAsync } from './factory' import { invokeWebApiWrapperAsync } from './factory'
import { UserView } from './Views' import { UserView } from './Views'
import { getUserId, getUserLogin } from '../utils/storage'
const formLayout: FormProps = { labelCol: { span: 11 }, wrapperCol: { span: 16 } } const formLayout: FormProps = { labelCol: { span: 11 }, wrapperCol: { span: 16 } }
@ -38,11 +39,11 @@ export const ChangePassword = memo<ChangePasswordProps>(({ user, visible, onCanc
const onFormFinish = () => invokeWebApiWrapperAsync( const onFormFinish = () => invokeWebApiWrapperAsync(
async() => { async() => {
await AuthService.changePassword(user?.id ?? localStorage['userId'], `"${password}"`) await AuthService.changePassword(user?.id ?? getUserId() ?? -1, `"${password}"`)
onOk?.() onOk?.()
}, },
setShowLoader, setShowLoader,
`Не удалось сменить пароль пользователя ${localStorage['login']}` `Не удалось сменить пароль пользователя ${getUserLogin()}`
) )
return ( return (

View File

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import { Role, Permission, hasPermission, isInRole } from '../../utils/PermissionService' import { Role, Permission, hasPermission, isInRole } from '../../utils/permissions'
type PrivateContentProps = { type PrivateContentProps = {
roles?: Role[] | Role roles?: Role[] | Role
@ -8,4 +8,4 @@ type PrivateContentProps = {
} }
export const PrivateContent: React.FC<PrivateContentProps> = ({ permission, roles, children = null }) => export const PrivateContent: React.FC<PrivateContentProps> = ({ permission, roles, children = null }) =>
hasPermission(permission) || isInRole(roles) ? children : null hasPermission(permission) && isInRole(roles) ? children : null

View File

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import { Menu } from 'antd' import { Menu } from 'antd'
import { Role, Permission, hasPermission, isInRole } from '../../utils/PermissionService' import { Role, Permission, hasPermission, isInRole } from '../../utils/permissions'
type PrivateMenuItemProps = { type PrivateMenuItemProps = {

View File

@ -1,7 +1,8 @@
import React from 'react' import React from 'react'
import { StaticContext } from 'react-router' import { StaticContext } from 'react-router'
import { Route, Redirect, RouteComponentProps } from 'react-router-dom' import { Route, Redirect, RouteComponentProps } from 'react-router-dom'
import { Role, Permission, hasPermission, isInRole } from '../../utils/PermissionService' import { Role, Permission, hasPermission, isInRole } from '../../utils/permissions'
import { getUserToken } from '../../utils/storage'
type PrivateRouteProps = { type PrivateRouteProps = {
roles: Role[] | Role roles: Role[] | Role
@ -12,7 +13,7 @@ type PrivateRouteProps = {
} }
export const PrivateRoute: React.FC<PrivateRouteProps> = ({ permission, roles, component, children, ...other }) => { export const PrivateRoute: React.FC<PrivateRouteProps> = ({ permission, roles, component, children, ...other }) => {
const available = localStorage.getItem('token') && (hasPermission(permission) || isInRole(roles)) const available = getUserToken() && (hasPermission(permission) && isInRole(roles))
return ( return (
<Route {...other} <Route {...other}

View File

@ -18,7 +18,6 @@ export const EditableCell = ({
dataIndex, dataIndex,
input, input,
isRequired, isRequired,
title,
formItemClass, formItemClass,
formItemRules, formItemRules,
children, children,

View File

@ -3,14 +3,11 @@ import { Link, useHistory } from 'react-router-dom'
import { Button, Dropdown, Menu } from 'antd' import { Button, Dropdown, Menu } from 'antd'
import { UserOutlined } from '@ant-design/icons' import { UserOutlined } from '@ant-design/icons'
import { getUserLogin, removeUser } from '../utils/storage'
import { PrivateMenuItem } from './Private' import { PrivateMenuItem } from './Private'
import { ChangePassword } from './ChangePassword' import { ChangePassword } from './ChangePassword'
const handleLogout = () => {
localStorage.removeItem('login')
localStorage.removeItem('token')
}
type UserMenuProps = { type UserMenuProps = {
isAdmin?: boolean isAdmin?: boolean
} }
@ -36,7 +33,7 @@ export const UserMenu: React.FC<UserMenuProps> = ({ isAdmin }) => {
placement={'bottomRight'} placement={'bottomRight'}
overlay={( overlay={(
<Menu style={{ textAlign: 'right' }}> <Menu style={{ textAlign: 'right' }}>
<PrivateMenuItem roles={'admin'}> <PrivateMenuItem permission={'admin_panel'}>
{isAdmin ? ( {isAdmin ? (
<Link to={'/'}>Вернуться на сайт</Link> <Link to={'/'}>Вернуться на сайт</Link>
) : ( ) : (
@ -47,12 +44,12 @@ export const UserMenu: React.FC<UserMenuProps> = ({ isAdmin }) => {
<Link to={'/'} onClick={onChangePasswordClick}>Сменить пароль</Link> <Link to={'/'} onClick={onChangePasswordClick}>Сменить пароль</Link>
</Menu.Item> </Menu.Item>
<Menu.Item> <Menu.Item>
<Link to={'/login'} onClick={handleLogout}>Выход</Link> <Link to={'/login'} onClick={removeUser}>Выход</Link>
</Menu.Item> </Menu.Item>
</Menu> </Menu>
)} )}
> >
<Button icon={<UserOutlined/>}>{localStorage['login']}</Button> <Button icon={<UserOutlined/>}>{getUserLogin()}</Button>
</Dropdown> </Dropdown>
<ChangePassword <ChangePassword
visible={isModalVisible} visible={isModalVisible}

View File

@ -1,6 +1,7 @@
import { notification } from 'antd' import { notification } from 'antd'
import { Dispatch, SetStateAction } from 'react' import { Dispatch, SetStateAction } from 'react'
import { FileInfoDto } from '../services/api' import { FileInfoDto } from '../services/api'
import { getUserToken } from '../utils/storage'
const notificationTypeDictionary = new Map([ const notificationTypeDictionary = new Map([
['error' , { notifyInstance: notification.error , caption: 'Ошибка' }], ['error' , { notifyInstance: notification.error , caption: 'Ошибка' }],
@ -55,7 +56,7 @@ export const invokeWebApiWrapperAsync = async (
export const download = async (url: string, fileName?: string) => { export const download = async (url: string, fileName?: string) => {
const response = await fetch(url, { const response = await fetch(url, {
headers: { headers: {
Authorization: `Bearer ${localStorage['token']}` Authorization: `Bearer ${getUserToken()}`
}, },
method: 'Get' method: 'Get'
}) })
@ -83,7 +84,7 @@ export const download = async (url: string, fileName?: string) => {
export const upload = async (url: string, formData: FormData) => { export const upload = async (url: string, formData: FormData) => {
const response = await fetch(url, { const response = await fetch(url, {
headers: { headers: {
Authorization: `Bearer ${localStorage['token']}` Authorization: `Bearer ${getUserToken()}`
}, },
method: 'Post', method: 'Post',
body: formData, body: formData,

View File

@ -59,7 +59,7 @@ export const AdminPanel = () => {
<PrivateRoute permission={'permission_editor'} path={`${rootPath}/permission`} component={PermissionController} /> <PrivateRoute permission={'permission_editor'} path={`${rootPath}/permission`} component={PermissionController} />
<PrivateRoute path={`${rootPath}/visit_log`} component={VisitLog} /> <PrivateRoute path={`${rootPath}/visit_log`} component={VisitLog} />
<Route path={'/'}> <Route path={'/'}>
<Redirect to={`${rootPath}/deposit`}/> <Redirect to={`${rootPath}/visit_log`}/>
</Route> </Route>
</Switch> </Switch>
</Suspense> </Suspense>

View File

@ -4,7 +4,7 @@ import { Layout, Menu } from 'antd'
import { FolderOutlined } from '@ant-design/icons' import { FolderOutlined } from '@ant-design/icons'
import { Switch, useParams, Link, Route } from 'react-router-dom' import { Switch, useParams, Link, Route } from 'react-router-dom'
import { PrivateMenuItem } from '../../components/Private' import { PrivateMenuItem } from '../../components/Private'
import { isInRole } from '../../utils/PermissionService' import { isInRole } from '../../utils/permissions'
import DocumentsTemplate from './DocumentsTemplate' import DocumentsTemplate from './DocumentsTemplate'
const { Content } = Layout const { Content } = Layout

View File

@ -2,23 +2,18 @@ import { Card, Form, Input, Button } from 'antd'
import { UserOutlined, LockOutlined } from '@ant-design/icons' import { UserOutlined, LockOutlined } from '@ant-design/icons'
import { useState } from 'react' import { useState } from 'react'
import { Link, useHistory } from 'react-router-dom' import { Link, useHistory } from 'react-router-dom'
import { AuthService, OpenAPI } from '../services/api/'
import { AuthService } from '../services/api/'
import LoaderPortal from '../components/LoaderPortal' import LoaderPortal from '../components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../components/factory' import { invokeWebApiWrapperAsync } from '../components/factory'
import { loginRules, passwordRules } from '../utils/validationRules' import { loginRules, passwordRules } from '../utils/validationRules'
import { setUser } from '../utils/storage'
import '../styles/index.css' import '../styles/index.css'
import logo from '../images/logo_32.png' import logo from '../images/logo_32.png'
const logoIcon = <img src={logo} alt={'АСБ'} className={'logo'} width={130}/> const logoIcon = <img src={logo} alt={'АСБ'} className={'logo'} width={130}/>
const setUser = (user) => {
OpenAPI.TOKEN = user.token
localStorage['userId'] = user.id
localStorage['token'] = user.token
localStorage['login'] = user.login
}
export default function Login() { export default function Login() {
const history = useHistory() const history = useHistory()
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)

View File

@ -8,7 +8,7 @@ import AdminPanel from './AdminPanel'
export const Main = () => ( export const Main = () => (
<Switch> <Switch>
<PrivateRoute path={'/admin/:tab?'} roles={['admin']}> <PrivateRoute path={'/admin/:tab?'} permission={'admin_panel'}>
<AdminLayoutPortal title={'Администраторская панель'}> <AdminLayoutPortal title={'Администраторская панель'}>
<AdminPanel /> <AdminPanel />
</AdminLayoutPortal> </AdminLayoutPortal>

View File

@ -1,18 +1,12 @@
import { getArrayFromLocalStorage } from './storage' import { getUserPermissions, getUserRoles } from './storage'
export type Role = string export type Role = string
export type Permission = string export type Permission = string
export const getUserRoles = (): Role[] => getArrayFromLocalStorage<Role>('roles') ?? []
export const getUserPermissions = (): Permission[] =>
getArrayFromLocalStorage<Permission>('permissions') ?? []
export const hasPermission = (permission?: Permission): boolean => { export const hasPermission = (permission?: Permission): boolean => {
if (typeof permission !== 'string') return true if (typeof permission !== 'string') return true
if (localStorage.getItem('login') === 'dev') return true // TODO: Удалить строку return getUserPermissions().includes(permission)
return permission in getUserPermissions()
} }
export const isInRole = (roles?: Role[] | Role): boolean => { export const isInRole = (roles?: Role[] | Role): boolean => {
@ -20,8 +14,6 @@ export const isInRole = (roles?: Role[] | Role): boolean => {
roles = [roles] roles = [roles]
if (!roles?.length) return true if (!roles?.length) return true
if (localStorage.getItem('login') === 'dev') return true // TODO: Удалить строку
const user_roles = getUserRoles() const user_roles = getUserRoles()
return roles.some((role) => role in user_roles) return roles.some((role) => role in user_roles)
} }

View File

@ -1,5 +1,41 @@
import { OpenAPI, UserTokenDto } from '../services/api'
import { Role, Permission } from './permissions'
export enum StorageNames {
userId = 'userId',
token = 'token',
login = 'login',
permissions = 'permissions',
roles = 'roles',
}
export const getArrayFromLocalStorage = <T extends string = string>(name: string, sep: string | RegExp = ','): T[] | null => { export const getArrayFromLocalStorage = <T extends string = string>(name: string, sep: string | RegExp = ','): T[] | null => {
const raw = localStorage.getItem(name) const raw = localStorage.getItem(name)
if (!raw) return null if (!raw) return null
return raw.split(sep).map<T>(elm => elm as T) return raw.split(sep).map<T>(elm => elm as T)
} }
export const getUserRoles = (): Role[] => getArrayFromLocalStorage<Role>(StorageNames.roles) ?? []
export const getUserPermissions = (): Permission[] => getArrayFromLocalStorage<Permission>(StorageNames.permissions) ?? []
export const getUserId = () => Number(localStorage.getItem(StorageNames.userId)) || null
export const getUserLogin = () => localStorage.getItem(StorageNames.login)
export const getUserToken = () => localStorage.getItem(StorageNames.token)
export const setUser = (user: UserTokenDto) => {
OpenAPI.TOKEN = user.token ?? undefined
localStorage.setItem(StorageNames.userId, String(user.id))
localStorage.setItem(StorageNames.token, String(user.token))
localStorage.setItem(StorageNames.login, String(user.login))
if (user.permissions)
localStorage.setItem(StorageNames.permissions, user.permissions.map((permission) => permission.name).join(','))
if (user.roleNames)
localStorage.setItem(StorageNames.roles, user.roleNames.join(','))
}
export const removeUser = () => {
localStorage.removeItem(StorageNames.userId)
localStorage.removeItem(StorageNames.login)
localStorage.removeItem(StorageNames.token)
localStorage.removeItem(StorageNames.permissions)
localStorage.removeItem(StorageNames.roles)
}