mirror of
https://github.com/diced/zipline.git
synced 2025-12-23 07:28:57 -08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1089dcfc73 | ||
|
|
bbae2776f6 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "zipline-next",
|
||||
"version": "2.4.3",
|
||||
"version": "2.5.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@dicedtomato/colors": "^1.0.3",
|
||||
@@ -26,12 +26,14 @@
|
||||
"material-ui-dropzone": "^3.5.0",
|
||||
"next": "^9.5.4",
|
||||
"pg": "^8.4.1",
|
||||
"qrcode": "^1.4.4",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-redux": "^7.2.1",
|
||||
"redux": "^4.0.5",
|
||||
"redux-persist": "^6.0.0",
|
||||
"speakeasy": "^2.0.0",
|
||||
"toml-patch": "^0.2.3",
|
||||
"typeorm": "^0.2.28"
|
||||
},
|
||||
@@ -49,6 +51,7 @@
|
||||
"@types/crypto-js": "^3.1.47",
|
||||
"@types/mongodb": "^3.5.27",
|
||||
"@types/node": "^14.11.2",
|
||||
"@types/qrcode": "^1.3.5",
|
||||
"@types/react": "^16.9.49",
|
||||
"@types/react-redux": "^7.1.9",
|
||||
"@types/semver": "^7.3.4",
|
||||
|
||||
@@ -6,6 +6,10 @@ import Button from '@material-ui/core/Button';
|
||||
import Card from '@material-ui/core/Card';
|
||||
import CardContent from '@material-ui/core/CardContent';
|
||||
import CardActions from '@material-ui/core/CardActions';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import Snackbar from '@material-ui/core/Snackbar';
|
||||
import Alert from '@material-ui/lab/Alert';
|
||||
import makeStyles from '@material-ui/core/styles/makeStyles';
|
||||
@@ -26,8 +30,11 @@ export default function Login() {
|
||||
const classes = useStyles();
|
||||
const router = useRouter();
|
||||
const dispatch = useDispatch();
|
||||
const [error, setError] = useState(false);
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [authOpen, setAuthOpen] = useState(false);
|
||||
const [token, setToken] = useState('');
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const handleClose = (event, reason) => {
|
||||
@@ -37,17 +44,48 @@ export default function Login() {
|
||||
|
||||
const handleLogin = async () => {
|
||||
const d = await (
|
||||
await fetch('/api/user/login', {
|
||||
await fetch('/api/user/verify-login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password })
|
||||
})
|
||||
).json();
|
||||
if (!d.error) {
|
||||
dispatch({ type: UPDATE_USER, payload: d });
|
||||
if (d.mfa) {
|
||||
setAuthOpen(true);
|
||||
} else {
|
||||
const payload = await (
|
||||
await fetch('/api/user/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password })
|
||||
})
|
||||
).json();
|
||||
if (!payload.error) {
|
||||
dispatch({ type: UPDATE_USER, payload });
|
||||
dispatch({ type: LOGIN });
|
||||
router.push('/dash');
|
||||
}
|
||||
}
|
||||
} else setOpen(true);
|
||||
};
|
||||
|
||||
const tryChecking = async () => {
|
||||
const verified = await (
|
||||
await fetch(`/api/mfa/verify?token=${token}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password })
|
||||
})
|
||||
).json();
|
||||
if (!verified.passed) setError(true);
|
||||
else {
|
||||
setError(false);
|
||||
setAuthOpen(false);
|
||||
dispatch({ type: UPDATE_USER, payload: verified.user });
|
||||
dispatch({ type: LOGIN });
|
||||
router.push('/dash');
|
||||
} else setOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -65,6 +103,29 @@ export default function Login() {
|
||||
Could not login!
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
<Dialog
|
||||
open={authOpen}
|
||||
onClose={() => setAuthOpen(false)}
|
||||
aria-labelledby='enable-mfa'
|
||||
aria-describedby='mfa-desc'
|
||||
>
|
||||
<DialogTitle id='enable-mfa'>2FA</DialogTitle>
|
||||
<DialogContent>
|
||||
<TextField
|
||||
label='Code'
|
||||
helperText={error ? 'Incorrect code' : ''}
|
||||
value={token}
|
||||
className={classes.field}
|
||||
onChange={e => setToken(e.target.value)}
|
||||
error={error}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button color='primary' autoFocus onClick={tryChecking}>
|
||||
Check
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<Card>
|
||||
<form>
|
||||
<CardContent>
|
||||
|
||||
@@ -3,14 +3,21 @@ import Typography from '@material-ui/core/Typography';
|
||||
import Card from '@material-ui/core/Card';
|
||||
import CardContent from '@material-ui/core/CardContent';
|
||||
import CardActions from '@material-ui/core/CardActions';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import Snackbar from '@material-ui/core/Snackbar';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import Alert from '@material-ui/lab/Alert';
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { UPDATE_USER } from '../reducer';
|
||||
import { store } from '../store';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { Config } from '../lib/Config';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
margin: {
|
||||
@@ -27,28 +34,70 @@ const useStyles = makeStyles({
|
||||
}
|
||||
});
|
||||
|
||||
export default function ManageUser() {
|
||||
export default function ManageUser({ config }: { config: Config }) {
|
||||
const classes = useStyles();
|
||||
const dispatch = useDispatch();
|
||||
const state = store.getState();
|
||||
const [alertOpen, setAlertOpen] = useState(false);
|
||||
const [mfaDialogOpen, setMfaDialogOpen] = useState(false);
|
||||
const [qrcode, setQRCode] = useState(null);
|
||||
const [username, setUsername] = useState(state.user.username);
|
||||
const [password, setPassword] = useState('');
|
||||
const [mfaToken, setMfaToken] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
const handleUpdateUser = async () => {
|
||||
const d = await (
|
||||
await fetch('/api/user', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password: password.trim() === '' ? null : password, email })
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
password: password.trim() === '' ? null : password,
|
||||
email
|
||||
})
|
||||
})
|
||||
).json();
|
||||
|
||||
if (!d.error) {
|
||||
dispatch({ type: UPDATE_USER, payload: d });
|
||||
setAlertOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
const disableMFA = async () => {
|
||||
await fetch('/api/mfa/disable');
|
||||
const d = await (await fetch('/api/user')).json();
|
||||
|
||||
if (!d.error) {
|
||||
dispatch({ type: UPDATE_USER, payload: d });
|
||||
setAlertOpen(true);
|
||||
}
|
||||
};
|
||||
const enableMFA = async () => {
|
||||
setMfaDialogOpen(true);
|
||||
const { dataURL } = await (await fetch('/api/mfa/qrcode')).json();
|
||||
setQRCode(dataURL);
|
||||
};
|
||||
const tryEnablingMfa = async () => {
|
||||
const verified = await (
|
||||
await fetch(`/api/mfa/verify?token=${mfaToken}`)
|
||||
).json();
|
||||
if (!verified) setError(true);
|
||||
else {
|
||||
setError(false);
|
||||
setMfaDialogOpen(false);
|
||||
setAlertOpen(true);
|
||||
const d = await (await fetch('/api/user')).json();
|
||||
|
||||
if (!d.error) {
|
||||
dispatch({ type: UPDATE_USER, payload: d });
|
||||
setAlertOpen(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Snackbar
|
||||
@@ -64,6 +113,41 @@ export default function ManageUser() {
|
||||
Updated <b>{state.user.username}</b>
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
<Dialog
|
||||
open={mfaDialogOpen}
|
||||
onClose={() => setMfaDialogOpen(false)}
|
||||
aria-labelledby='enable-mfa'
|
||||
aria-describedby='mfa-desc'
|
||||
>
|
||||
<DialogTitle id='enable-mfa'>Enable 2FA</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id='mfa-desc'>
|
||||
When enabling 2FA/MFA you can use <b>Authy</b> or{' '}
|
||||
<b>Google Authenticator</b> to authenticate before logging into
|
||||
Zipline.
|
||||
</DialogContentText>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<img src={qrcode} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<TextField
|
||||
label='Code'
|
||||
helperText='After scanning the QRCode, enter the authentication code here'
|
||||
value={mfaToken}
|
||||
className={classes.field}
|
||||
onChange={e => setMfaToken(e.target.value)}
|
||||
error={error}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button color='primary' autoFocus onClick={tryEnablingMfa}>
|
||||
Enable
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Typography color='textSecondary' variant='h4' gutterBottom>
|
||||
@@ -100,6 +184,15 @@ export default function ManageUser() {
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
{config.core.mfa ? (
|
||||
<Button
|
||||
className={classes.button}
|
||||
color='primary'
|
||||
onClick={state.user.secretMfaKey ? disableMFA : enableMFA}
|
||||
>
|
||||
{state.user.secretMfaKey ? 'Disable MFA' : 'Enable MFA'}
|
||||
</Button>
|
||||
) : null}
|
||||
</CardActions>
|
||||
</Card>
|
||||
</React.Fragment>
|
||||
|
||||
@@ -23,6 +23,7 @@ import { Image } from './lib/entities/Image';
|
||||
import { User } from './lib/entities/User';
|
||||
import { Zipline } from './lib/entities/Zipline';
|
||||
import { URL } from './lib/entities/URL';
|
||||
import { MultiFactorController } from './lib/api/controllers/MultiFactorController';
|
||||
const dev = process.env.NODE_ENV !== 'production';
|
||||
|
||||
(async () => {
|
||||
@@ -159,7 +160,8 @@ server.register(bootstrap, {
|
||||
UserController,
|
||||
RootController,
|
||||
ImagesController,
|
||||
URLSController
|
||||
URLSController,
|
||||
MultiFactorController
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ export interface ConfigCore {
|
||||
blacklisted_ips?: string[];
|
||||
ratelimiter?: ConfigCoreRateLimiter;
|
||||
theme?: 'dark' | 'light';
|
||||
mfa?: boolean;
|
||||
}
|
||||
|
||||
export interface ConfigWebhooks {
|
||||
|
||||
127
src/lib/api/controllers/MultiFactorController.ts
Normal file
127
src/lib/api/controllers/MultiFactorController.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { FastifyReply, FastifyRequest, FastifyInstance } from 'fastify';
|
||||
import {
|
||||
Controller,
|
||||
GET,
|
||||
FastifyInstanceToken,
|
||||
Inject,
|
||||
POST
|
||||
} from 'fastify-decorators';
|
||||
import { Repository } from 'typeorm';
|
||||
import { User } from '../../entities/User';
|
||||
import { totp, generateSecret, otpauthURL } from 'speakeasy';
|
||||
import { toDataURL } from 'qrcode';
|
||||
import { checkPassword, createBaseCookie, readBaseCookie } from '../../Util';
|
||||
import { LoginError, MissingBodyData, UserNotFoundError } from '../APIErrors';
|
||||
import { Console } from '../../logger';
|
||||
import { WebhookType, WebhookHelper } from '../../Webhooks';
|
||||
import { Configuration, ConfigWebhooks } from '../../Config';
|
||||
|
||||
const config = Configuration.readConfig();
|
||||
|
||||
@Controller('/api/mfa')
|
||||
export class MultiFactorController {
|
||||
@Inject(FastifyInstanceToken)
|
||||
private instance!: FastifyInstance;
|
||||
|
||||
private users: Repository<User> = this.instance.orm.getRepository(User);
|
||||
private logger: Console = Console.logger(User);
|
||||
private webhooks: ConfigWebhooks = WebhookHelper.conf(config);
|
||||
|
||||
@GET('/qrcode')
|
||||
async qrcode(req: FastifyRequest, reply: FastifyReply) {
|
||||
if (!req.cookies.zipline) throw new LoginError('Not logged in.');
|
||||
let user = await this.users.findOne({
|
||||
where: {
|
||||
id: readBaseCookie(req.cookies.zipline)
|
||||
}
|
||||
});
|
||||
|
||||
if (!user.secretMfaKey) {
|
||||
const secret = generateSecret({
|
||||
issuer: 'Zipline',
|
||||
length: 128,
|
||||
name: user.username
|
||||
});
|
||||
user.secretMfaKey = secret.base32;
|
||||
user = await this.users.save(user);
|
||||
}
|
||||
|
||||
const dataURL = await toDataURL(
|
||||
otpauthURL({
|
||||
secret: user.secretMfaKey,
|
||||
label: user.email || 'none',
|
||||
issuer: 'Zipline',
|
||||
encoding: 'base32'
|
||||
})
|
||||
);
|
||||
|
||||
return reply.send({
|
||||
dataURL
|
||||
});
|
||||
}
|
||||
|
||||
@GET('/disable')
|
||||
async disable(req: FastifyRequest, reply: FastifyReply) {
|
||||
if (!req.cookies.zipline) throw new LoginError('Not logged in.');
|
||||
const user = await this.users.findOne({
|
||||
where: {
|
||||
id: readBaseCookie(req.cookies.zipline)
|
||||
}
|
||||
});
|
||||
|
||||
user.secretMfaKey = null;
|
||||
|
||||
this.users.save(user);
|
||||
|
||||
reply.send({ disabled: true });
|
||||
}
|
||||
|
||||
@POST('/verify')
|
||||
async verify(
|
||||
req: FastifyRequest<{
|
||||
Querystring: { token: string };
|
||||
Body: { username: string; password: string };
|
||||
}>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
if (req.cookies.zipline) throw new LoginError('Already logged in.');
|
||||
if (!req.body.username) throw new MissingBodyData('Missing username.');
|
||||
if (!req.body.password) throw new MissingBodyData('Missing uassword.');
|
||||
|
||||
const user = await this.users.findOne({
|
||||
where: {
|
||||
username: req.body.username
|
||||
}
|
||||
});
|
||||
|
||||
if (!user)
|
||||
throw new UserNotFoundError(`User "${req.body.username}" was not found.`);
|
||||
if (!checkPassword(req.body.password, user.password)) {
|
||||
this.logger.error(
|
||||
`${user.username} (${user.id}) tried to login but failed`
|
||||
);
|
||||
throw new LoginError('Wrong credentials!');
|
||||
}
|
||||
delete user.password;
|
||||
|
||||
const passed = totp.verify({
|
||||
encoding: 'base32',
|
||||
token: req.query.token,
|
||||
secret: user.secretMfaKey
|
||||
});
|
||||
|
||||
this.logger.verbose(`set cookie for ${user.username} (${user.id})`);
|
||||
reply.setCookie('zipline', createBaseCookie(user.id), {
|
||||
path: '/',
|
||||
maxAge: 1036800000
|
||||
});
|
||||
|
||||
this.logger.info(`${user.username} (${user.id}) logged in`);
|
||||
if (this.webhooks.events.includes(WebhookType.LOGIN))
|
||||
WebhookHelper.sendWebhook(this.webhooks.login.content, {
|
||||
user
|
||||
});
|
||||
|
||||
return reply.send({ user, passed });
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,9 @@ export class UserController {
|
||||
|
||||
@PATCH('/')
|
||||
async editUser(
|
||||
req: FastifyRequest<{ Body: { username: string; password: string, email: string; } }>,
|
||||
req: FastifyRequest<{
|
||||
Body: { username: string; password: string; email: string };
|
||||
}>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
if (!req.cookies.zipline) throw new LoginError('Not logged in.');
|
||||
@@ -92,6 +94,35 @@ export class UserController {
|
||||
return reply.send(user);
|
||||
}
|
||||
|
||||
@POST('/verify-login')
|
||||
async verify(
|
||||
req: FastifyRequest<{ Body: { username: string; password: string } }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
if (req.cookies.zipline) throw new LoginError('Already logged in.');
|
||||
if (!req.body.username) throw new MissingBodyData('Missing username.');
|
||||
if (!req.body.password) throw new MissingBodyData('Missing uassword.');
|
||||
|
||||
const user = await this.users.findOne({
|
||||
where: {
|
||||
username: req.body.username
|
||||
}
|
||||
});
|
||||
|
||||
if (!user)
|
||||
throw new UserNotFoundError(`User "${req.body.username}" was not found.`);
|
||||
if (!checkPassword(req.body.password, user.password)) {
|
||||
this.logger.error(
|
||||
`${user.username} (${user.id}) tried to verify their credentials but failed`
|
||||
);
|
||||
throw new LoginError('Wrong credentials!');
|
||||
}
|
||||
|
||||
reply.send({
|
||||
mfa: !!user.secretMfaKey
|
||||
});
|
||||
}
|
||||
|
||||
@POST('/login')
|
||||
async login(
|
||||
req: FastifyRequest<{ Body: { username: string; password: string } }>,
|
||||
@@ -250,7 +281,6 @@ export class UserController {
|
||||
throw new Error(`Could not delete user: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// @Hook('preValidation')
|
||||
// public async preValidation(req: FastifyRequest, reply: FastifyReply) {
|
||||
// // const adminRoutes = ['/api/user/create'];
|
||||
|
||||
@@ -11,7 +11,7 @@ export class User {
|
||||
@Column('text')
|
||||
public password: string;
|
||||
|
||||
@Column('text', { default: null }) /* used for gravatar avatar! */
|
||||
@Column('text', { default: null }) /* used for gravatar avatar! */
|
||||
public email: string;
|
||||
|
||||
@Column('boolean', { default: false })
|
||||
@@ -20,6 +20,9 @@ export class User {
|
||||
@Column('text')
|
||||
public token: string;
|
||||
|
||||
@Column('simple-json', { default: null })
|
||||
public secretMfaKey: string;
|
||||
|
||||
public constructor(
|
||||
username: string,
|
||||
password: string,
|
||||
@@ -31,5 +34,6 @@ export class User {
|
||||
this.email = null;
|
||||
this.administrator = administrator;
|
||||
this.token = token;
|
||||
this.secretMfaKey = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,18 +4,25 @@ import UI from '../../components/UI';
|
||||
import UIPlaceholder from '../../components/UIPlaceholder';
|
||||
import ManageUser from '../../components/ManageUser';
|
||||
import { store } from '../../store';
|
||||
import { Configuration } from '../../lib/Config';
|
||||
|
||||
export default function Manage() {
|
||||
export default function Manage({ config }) {
|
||||
const router = useRouter();
|
||||
const state = store.getState();
|
||||
|
||||
if (typeof window !== 'undefined' && !state.loggedIn) router.push('/user/login');
|
||||
if (typeof window !== 'undefined' && !state.loggedIn)
|
||||
router.push('/user/login');
|
||||
else {
|
||||
return (
|
||||
<UI>
|
||||
<ManageUser />
|
||||
<ManageUser config={config} />
|
||||
</UI>
|
||||
);
|
||||
}
|
||||
return <UIPlaceholder />;
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
const config = Configuration.readConfig();
|
||||
return { props: { config } };
|
||||
}
|
||||
|
||||
113
yarn.lock
113
yarn.lock
@@ -1316,6 +1316,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
|
||||
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
|
||||
|
||||
"@types/qrcode@^1.3.5":
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/qrcode/-/qrcode-1.3.5.tgz#9c97cc2875f03e2b16a0d89856fc48414e380c38"
|
||||
integrity sha512-92QMnMb9m0ErBU20za5Eqtf4lzUcSkk5w/Cz30q5qod0lWHm2loztmFs2EnCY06yT51GY1+m/oFq2D8qVK2Bjg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/react-redux@^7.1.9":
|
||||
version "7.1.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.11.tgz#a18e8ab3651e8e8cc94798934927937c66021217"
|
||||
@@ -1917,6 +1924,11 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
base32.js@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/base32.js/-/base32.js-0.0.1.tgz#d045736a57b1f6c139f0c7df42518a84e91bb2ba"
|
||||
integrity sha1-0EVzalex9sE58MffQlGKhOkbsro=
|
||||
|
||||
base64-js@^1.0.2, base64-js@^1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
|
||||
@@ -2110,6 +2122,24 @@ bson@^1.1.4:
|
||||
resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.5.tgz#2aaae98fcdf6750c0848b0cba1ddec3c73060a34"
|
||||
integrity sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==
|
||||
|
||||
buffer-alloc-unsafe@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
|
||||
integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
|
||||
|
||||
buffer-alloc@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
|
||||
integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
|
||||
dependencies:
|
||||
buffer-alloc-unsafe "^1.1.0"
|
||||
buffer-fill "^1.0.0"
|
||||
|
||||
buffer-fill@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
|
||||
integrity sha1-+PeLdniYiO858gXNY39o5wISKyw=
|
||||
|
||||
buffer-from@^1.0.0, buffer-from@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
@@ -2142,7 +2172,7 @@ buffer@^4.3.0:
|
||||
ieee754 "^1.1.4"
|
||||
isarray "^1.0.0"
|
||||
|
||||
buffer@^5.5.0:
|
||||
buffer@^5.4.3, buffer@^5.5.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
||||
@@ -2406,6 +2436,15 @@ cli-width@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
|
||||
integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
|
||||
|
||||
cliui@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
|
||||
integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==
|
||||
dependencies:
|
||||
string-width "^3.1.0"
|
||||
strip-ansi "^5.2.0"
|
||||
wrap-ansi "^5.1.0"
|
||||
|
||||
cliui@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
|
||||
@@ -2889,6 +2928,11 @@ diffie-hellman@^5.0.0:
|
||||
miller-rabin "^4.0.0"
|
||||
randombytes "^2.0.0"
|
||||
|
||||
dijkstrajs@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.1.tgz#d3cd81221e3ea40742cfcde556d4e99e98ddc71b"
|
||||
integrity sha1-082BIh4+pAdCz83lVtTpnpjdxxs=
|
||||
|
||||
dir-glob@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
|
||||
@@ -4347,6 +4391,11 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
|
||||
|
||||
isarray@^2.0.1:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
|
||||
integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
|
||||
|
||||
isexe@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
@@ -5576,6 +5625,11 @@ platform@1.3.3:
|
||||
resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.3.tgz#646c77011899870b6a0903e75e997e8e51da7461"
|
||||
integrity sha1-ZGx3ARiZhwtqCQPnXpl+jlHadGE=
|
||||
|
||||
pngjs@^3.3.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f"
|
||||
integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==
|
||||
|
||||
pnp-webpack-plugin@1.6.4:
|
||||
version "1.6.4"
|
||||
resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149"
|
||||
@@ -5801,6 +5855,19 @@ punycode@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
|
||||
qrcode@^1.4.4:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.4.4.tgz#f0c43568a7e7510a55efc3b88d9602f71963ea83"
|
||||
integrity sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q==
|
||||
dependencies:
|
||||
buffer "^5.4.3"
|
||||
buffer-alloc "^1.2.0"
|
||||
buffer-from "^1.1.1"
|
||||
dijkstrajs "^1.0.1"
|
||||
isarray "^2.0.1"
|
||||
pngjs "^3.3.0"
|
||||
yargs "^13.2.4"
|
||||
|
||||
querystring-es3@^0.2.0:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
|
||||
@@ -6538,6 +6605,13 @@ sparse-bitfield@^3.0.3:
|
||||
dependencies:
|
||||
memory-pager "^1.0.2"
|
||||
|
||||
speakeasy@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/speakeasy/-/speakeasy-2.0.0.tgz#85c91a071b09a5cb8642590d983566165f57613a"
|
||||
integrity sha1-hckaBxsJpcuGQlkNmDVmFl9XYTo=
|
||||
dependencies:
|
||||
base32.js "0.0.1"
|
||||
|
||||
split-string@^3.0.1, split-string@^3.0.2:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
|
||||
@@ -6668,7 +6742,7 @@ string-width@^1.0.1:
|
||||
is-fullwidth-code-point "^2.0.0"
|
||||
strip-ansi "^4.0.0"
|
||||
|
||||
string-width@^3.0.0:
|
||||
string-width@^3.0.0, string-width@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
|
||||
integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
|
||||
@@ -6749,7 +6823,7 @@ strip-ansi@^4.0.0:
|
||||
dependencies:
|
||||
ansi-regex "^3.0.0"
|
||||
|
||||
strip-ansi@^5.1.0:
|
||||
strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
|
||||
integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
|
||||
@@ -7364,6 +7438,15 @@ worker-farm@^1.7.0:
|
||||
dependencies:
|
||||
errno "~0.1.7"
|
||||
|
||||
wrap-ansi@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09"
|
||||
integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==
|
||||
dependencies:
|
||||
ansi-styles "^3.2.0"
|
||||
string-width "^3.0.0"
|
||||
strip-ansi "^5.0.0"
|
||||
|
||||
wrap-ansi@^6.2.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
|
||||
@@ -7441,6 +7524,14 @@ yargonaut@^1.1.2:
|
||||
figlet "^1.1.1"
|
||||
parent-require "^1.0.0"
|
||||
|
||||
yargs-parser@^13.1.2:
|
||||
version "13.1.2"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"
|
||||
integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==
|
||||
dependencies:
|
||||
camelcase "^5.0.0"
|
||||
decamelize "^1.2.0"
|
||||
|
||||
yargs-parser@^18.1.2:
|
||||
version "18.1.3"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
|
||||
@@ -7454,6 +7545,22 @@ yargs-parser@^20.2.2:
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.3.tgz#92419ba867b858c868acf8bae9bf74af0dd0ce26"
|
||||
integrity sha512-emOFRT9WVHw03QSvN5qor9QQT9+sw5vwxfYweivSMHTcAXPefwVae2FjO7JJjj8hCE4CzPOPeFM83VwT29HCww==
|
||||
|
||||
yargs@^13.2.4:
|
||||
version "13.3.2"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
|
||||
integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==
|
||||
dependencies:
|
||||
cliui "^5.0.0"
|
||||
find-up "^3.0.0"
|
||||
get-caller-file "^2.0.1"
|
||||
require-directory "^2.1.1"
|
||||
require-main-filename "^2.0.0"
|
||||
set-blocking "^2.0.0"
|
||||
string-width "^3.0.0"
|
||||
which-module "^2.0.0"
|
||||
y18n "^4.0.0"
|
||||
yargs-parser "^13.1.2"
|
||||
|
||||
yargs@^15.0.0:
|
||||
version "15.4.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
|
||||
|
||||
Reference in New Issue
Block a user