aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Tananaev <anton@traccar.org>2022-08-02 19:16:10 -0700
committerAnton Tananaev <anton@traccar.org>2022-08-02 19:16:10 -0700
commitb2dd52831a19cae8d6d46ee1733bde02ad4ca08b (patch)
treef27b33610a5cca126e2c2df6a40df5c537ddbbdc
parent114c78dff8fbfdc19444c88fea45188012065a3a (diff)
downloadtrackermap-web-b2dd52831a19cae8d6d46ee1733bde02ad4ca08b.tar.gz
trackermap-web-b2dd52831a19cae8d6d46ee1733bde02ad4ca08b.tar.bz2
trackermap-web-b2dd52831a19cae8d6d46ee1733bde02ad4ca08b.zip
New API token system
-rw-r--r--modern/src/settings/PreferencesPage.js48
-rw-r--r--modern/src/settings/UserPage.js27
-rw-r--r--web/app/model/User.js3
-rw-r--r--web/app/view/dialog/User.js11
-rw-r--r--web/app/view/dialog/UserController.js12
5 files changed, 47 insertions, 54 deletions
diff --git a/modern/src/settings/PreferencesPage.js b/modern/src/settings/PreferencesPage.js
index 11cf860c..3b0c7e9c 100644
--- a/modern/src/settings/PreferencesPage.js
+++ b/modern/src/settings/PreferencesPage.js
@@ -1,11 +1,13 @@
-import React from 'react';
+import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import {
- Accordion, AccordionSummary, AccordionDetails, Typography, Container, FormControl, InputLabel, Select, MenuItem, Checkbox, FormControlLabel, FormGroup,
+ Accordion, AccordionSummary, AccordionDetails, Typography, Container, FormControl, InputLabel, Select, MenuItem, Checkbox, FormControlLabel, FormGroup, InputAdornment, IconButton, OutlinedInput,
} from '@mui/material';
import makeStyles from '@mui/styles/makeStyles';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import CachedIcon from '@mui/icons-material/Cached';
+import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import { useLocalization, useTranslation, useTranslationKeys } from '../common/components/LocalizationProvider';
import usePersistedState from '../common/util/usePersistedState';
import PageLayout from '../common/components/PageLayout';
@@ -15,6 +17,7 @@ import { prefixString, unprefixString } from '../common/util/stringUtils';
import SelectField from '../common/components/SelectField';
import useMapStyles from '../map/core/useMapStyles';
import useMapOverlays from '../map/overlay/useMapOverlays';
+import { useCatch } from '../reactHelper';
const useStyles = makeStyles((theme) => ({
container: {
@@ -26,6 +29,10 @@ const useStyles = makeStyles((theme) => ({
gap: theme.spacing(2),
paddingBottom: theme.spacing(3),
},
+ tokenActions: {
+ display: 'flex',
+ flexDirection: 'column',
+ },
}));
const PreferencesPage = () => {
@@ -38,6 +45,8 @@ const PreferencesPage = () => {
const { languages, language, setLanguage } = useLocalization();
const languageList = Object.entries(languages).map((values) => ({ code: values[0], name: values[1].name }));
+ const [token, setToken] = useState();
+
const mapStyles = useMapStyles();
const [activeMapStyles, setActiveMapStyles] = usePersistedState('activeMapStyles', ['locationIqStreets', 'osm', 'carto']);
@@ -53,6 +62,18 @@ const PreferencesPage = () => {
const [mapCluster, setMapCluster] = usePersistedState('mapCluster', true);
const [mapOnSelect, setMapOnSelect] = usePersistedState('mapOnSelect', false);
+ const generateToken = useCatch(async () => {
+ const response = await fetch('/api/session/token', {
+ method: 'POST',
+ body: new URLSearchParams(),
+ });
+ if (response.ok) {
+ setToken(await response.text());
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
const alarms = useTranslationKeys((it) => it.startsWith('alarm')).map((it) => ({
key: unprefixString('alarm', it),
name: t(it),
@@ -81,6 +102,29 @@ const PreferencesPage = () => {
{languageList.map((it) => <MenuItem key={it.code} value={it.code}>{it.name}</MenuItem>)}
</Select>
</FormControl>
+ <FormControl>
+ <InputLabel>{t('userToken')}</InputLabel>
+ <OutlinedInput
+ multiline
+ rows={6}
+ readOnly
+ type="text"
+ label={t('userToken')}
+ value={token || ''}
+ endAdornment={(
+ <InputAdornment position="end">
+ <div className={classes.tokenActions}>
+ <IconButton size="small" onClick={generateToken} disabled={!!token}>
+ <CachedIcon fontSize="small" />
+ </IconButton>
+ <IconButton size="small" onClick={() => navigator.clipboard.writeText(token)} disabled={!token}>
+ <ContentCopyIcon fontSize="small" />
+ </IconButton>
+ </div>
+ </InputAdornment>
+ )}
+ />
+ </FormControl>
</AccordionDetails>
</Accordion>
<Accordion>
diff --git a/modern/src/settings/UserPage.js b/modern/src/settings/UserPage.js
index 85f4ae34..4dc6be95 100644
--- a/modern/src/settings/UserPage.js
+++ b/modern/src/settings/UserPage.js
@@ -11,16 +11,12 @@ import {
MenuItem,
FormControlLabel,
Checkbox,
- InputAdornment,
- IconButton,
- OutlinedInput,
FormGroup,
TextField,
Button,
} from '@mui/material';
import makeStyles from '@mui/styles/makeStyles';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
-import CachedIcon from '@mui/icons-material/Cached';
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
import { useDispatch, useSelector } from 'react-redux';
import moment from 'moment';
@@ -263,28 +259,7 @@ const UserPage = () => {
</Typography>
</AccordionSummary>
<AccordionDetails className={classes.details}>
- <FormControl>
- <InputLabel>{t('userToken')}</InputLabel>
- <OutlinedInput
- type="text"
- label={t('userToken')}
- value={item.token || ''}
- onChange={(e) => setItem({ ...item, token: e.target.value })}
- endAdornment={(
- <InputAdornment position="end">
- <IconButton
- size="small"
- onClick={() => {
- const token = [...Array(30)].map(() => Math.random().toString(36)[2]).join('');
- setItem({ ...item, token });
- }}
- >
- <CachedIcon fontSize="small" />
- </IconButton>
- </InputAdornment>
- )}
- />
- </FormControl>
+ token
<TextField
label={t('userExpirationTime')}
type="date"
diff --git a/web/app/model/User.js b/web/app/model/User.js
index fcaff007..455bca64 100644
--- a/web/app/model/User.js
+++ b/web/app/model/User.js
@@ -87,9 +87,6 @@ Ext.define('Traccar.model.User', {
name: 'poiLayer',
type: 'string'
}, {
- name: 'token',
- type: 'string'
- }, {
name: 'attributes'
}],
diff --git a/web/app/view/dialog/User.js b/web/app/view/dialog/User.js
index 7335ee79..5da56424 100644
--- a/web/app/view/dialog/User.js
+++ b/web/app/view/dialog/User.js
@@ -172,17 +172,6 @@ Ext.define('Traccar.view.dialog.User', {
fieldLabel: Strings.userUserLimit,
disabled: true,
reference: 'userLimitField'
- }, {
- xtype: 'unescapedTextField',
- name: 'token',
- reference: 'tokenField',
- fieldLabel: Strings.userToken,
- triggers: {
- generate: {
- cls: 'iconCls: x-fa fa-refresh',
- handler: 'generateToken'
- }
- }
}]
}]
},
diff --git a/web/app/view/dialog/UserController.js b/web/app/view/dialog/UserController.js
index 4ba48563..0d620614 100644
--- a/web/app/view/dialog/UserController.js
+++ b/web/app/view/dialog/UserController.js
@@ -35,18 +35,6 @@ Ext.define('Traccar.view.dialog.UserController', {
}
},
- symbols: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
-
- generateToken: function () {
- var i, newToken = '';
-
- for (i = 0; i < 32; i++) {
- newToken += this.symbols.charAt(Math.floor(Math.random() * this.symbols.length));
- }
-
- this.lookupReference('tokenField').setValue(newToken);
- },
-
testNotification: function () {
Ext.Ajax.request({
url: 'api/notifications/test',