mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) Remove LoginSession, which was mainly serving situations that are no longer used.
Summary: In the past, Cognito sign-ins were intended to give authorization to some AWS services (like SQS); various tokens were stored in the session for this purpose. This is no longer used. Profiles from Cognito now serve a limited purpose: first-time initialization of name and picture, and keeping track of which login method was used. For these remaining needs, ScopedSession is sufficient. Test Plan: Existing test pass. Tested manually that logins work with Google and Email + Password. Tested manually that on a clean database, name and picture are picked up from a Google Login. Reviewers: paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D2907
This commit is contained in:
parent
f079ffdcb3
commit
869b2f00ec
4
app/server/declarations.d.ts
vendored
4
app/server/declarations.d.ts
vendored
@ -4,7 +4,7 @@ declare module "app/server/lib/User";
|
||||
|
||||
declare module "app/server/lib/Comm" {
|
||||
import {Client, ClientMethod} from "app/server/lib/Client";
|
||||
import {LoginSession} from "app/server/lib/LoginSession";
|
||||
import {ScopedSession} from "app/server/lib/BrowserSession";
|
||||
import * as http from "http";
|
||||
|
||||
class Comm {
|
||||
@ -14,7 +14,7 @@ declare module "app/server/lib/Comm" {
|
||||
public setServerVersion(serverVersion: string|null): void;
|
||||
public setServerActivation(active: boolean): void;
|
||||
public getSessionIdFromCookie(gristSidCookie: string): string;
|
||||
public getOrCreateSession(sessionId: string, req: any): LoginSession;
|
||||
public getOrCreateSession(sessionId: string, req: any): ScopedSession;
|
||||
public registerMethods(methods: {[name: string]: ClientMethod}): void;
|
||||
public getClient(clientId: string): Client;
|
||||
public testServerShutdown(): Promise<void>;
|
||||
|
@ -1164,7 +1164,7 @@ export class ActiveDoc extends EventEmitter {
|
||||
await this._granularAccess.assertCanMaybeApplyUserActions(docSession, actions);
|
||||
|
||||
const user = docSession.mode === 'system' ? 'grist' :
|
||||
(client && client.session ? (await client.session.getEmail()) : "");
|
||||
(client?.getProfile()?.email || '');
|
||||
|
||||
// Create the UserActionBundle.
|
||||
const action: UserActionBundle = {
|
||||
|
@ -8,19 +8,19 @@ export interface SessionUserObj {
|
||||
// a grist-internal identify for the user, if known.
|
||||
userId?: number;
|
||||
|
||||
// The user profile object. When updated, all clients get a message with the update.
|
||||
// The user profile object.
|
||||
profile?: UserProfile;
|
||||
|
||||
// Authentication provider string indicating the login method used.
|
||||
// [UNUSED] Authentication provider string indicating the login method used.
|
||||
authProvider?: string;
|
||||
|
||||
// Login ID token used to access AWS services.
|
||||
// [UNUSED] Login ID token used to access AWS services.
|
||||
idToken?: string;
|
||||
|
||||
// Login access token used to access other AWS services.
|
||||
// [UNUSED] Login access token used to access other AWS services.
|
||||
accessToken?: string;
|
||||
|
||||
// Login refresh token used to retrieve new ID and access tokens.
|
||||
// [UNUSED] Login refresh token used to retrieve new ID and access tokens.
|
||||
refreshToken?: string;
|
||||
}
|
||||
|
||||
@ -133,6 +133,26 @@ export class ScopedSession {
|
||||
return getSessionUser(session, this._org, this._userSelector) || {};
|
||||
}
|
||||
|
||||
// Retrieves the user profile from the session.
|
||||
public async getSessionProfile(prev?: SessionObj): Promise<UserProfile|null> {
|
||||
return (await this.getScopedSession(prev)).profile || null;
|
||||
}
|
||||
|
||||
// Updates a user profile. The session may have multiple profiles associated with different
|
||||
// email addresses. This will update the one with a matching email address, or add a new one.
|
||||
// This is mainly used to know which emails are logged in in this session; fields like name and
|
||||
// picture URL come from the database instead.
|
||||
public async updateUserProfile(profile: UserProfile|null): Promise<void> {
|
||||
if (profile) {
|
||||
await this.operateOnScopedSession(async user => {
|
||||
user.profile = profile;
|
||||
return user;
|
||||
});
|
||||
} else {
|
||||
await this.clearScopedSession();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* This performs an operation on the session object, limited to a single user entry. The state of that
|
||||
|
@ -8,9 +8,9 @@ import {User} from 'app/gen-server/entity/User';
|
||||
import {HomeDBManager} from 'app/gen-server/lib/HomeDBManager';
|
||||
import {ActiveDoc} from 'app/server/lib/ActiveDoc';
|
||||
import {Authorizer} from 'app/server/lib/Authorizer';
|
||||
import {ScopedSession} from 'app/server/lib/BrowserSession';
|
||||
import {DocSession} from 'app/server/lib/DocSession';
|
||||
import * as log from 'app/server/lib/log';
|
||||
import {ILoginSession} from 'app/server/lib/ILoginSession';
|
||||
import {shortDesc} from 'app/server/lib/shortDesc';
|
||||
import * as crypto from 'crypto';
|
||||
import * as moment from 'moment';
|
||||
@ -61,10 +61,10 @@ void(MESSAGE_TYPES_NO_AUTH);
|
||||
export class Client {
|
||||
public readonly clientId: string;
|
||||
|
||||
public session: ILoginSession|null = null;
|
||||
|
||||
public browserSettings: BrowserSettings = {};
|
||||
|
||||
private _session: ScopedSession|null = null;
|
||||
|
||||
// Maps docFDs to DocSession objects.
|
||||
private _docFDs: Array<DocSession|null> = [];
|
||||
|
||||
@ -221,21 +221,16 @@ export class Client {
|
||||
}
|
||||
}
|
||||
|
||||
// Assigns the client to the given login session and the session to the client.
|
||||
public setSession(session: ILoginSession): void {
|
||||
this.unsetSession();
|
||||
this.session = session;
|
||||
session.clients.add(this);
|
||||
// Assigns the given ScopedSession to the client.
|
||||
public setSession(session: ScopedSession): void {
|
||||
this._session = session;
|
||||
}
|
||||
|
||||
// Unsets the current login session and removes the client from it.
|
||||
public unsetSession(): void {
|
||||
if (this.session) { this.session.clients.delete(this); }
|
||||
this.session = null;
|
||||
public getSession(): ScopedSession|null {
|
||||
return this._session;
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.unsetSession();
|
||||
this._destroyed = true;
|
||||
}
|
||||
|
||||
@ -318,6 +313,14 @@ export class Client {
|
||||
return this._profile;
|
||||
}
|
||||
|
||||
public async getSessionProfile(): Promise<UserProfile|null|undefined> {
|
||||
return this._session?.getSessionProfile();
|
||||
}
|
||||
|
||||
public async getSessionEmail(): Promise<string|null> {
|
||||
return (await this.getSessionProfile())?.email || null;
|
||||
}
|
||||
|
||||
public getCachedUserId(): number|null {
|
||||
return this._userId;
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ function Comm(server, options) {
|
||||
this._clients = {}; // Maps clientIds to Client objects.
|
||||
this.clientList = []; // List of all active Clients, ordered by clientId.
|
||||
|
||||
// Maps sessionIds to LoginSession objects.
|
||||
// Maps sessionIds to ScopedSession objects.
|
||||
this.sessions = options.sessions;
|
||||
|
||||
this._settings = options.settings;
|
||||
@ -118,14 +118,13 @@ Comm.prototype.getClient = function(clientId) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a LoginSession object with the given session id from the list of sessions,
|
||||
* Returns a ScopedSession object with the given session id from the list of sessions,
|
||||
* or adds a new one and returns that.
|
||||
*/
|
||||
Comm.prototype.getOrCreateSession = function(sid, req, userSelector) {
|
||||
// LoginSessions are specific to a session id / org combination.
|
||||
// ScopedSessions are specific to a session id / org combination.
|
||||
const org = req.org || "";
|
||||
return this.sessions.getOrCreateLoginSession(sid, org, this,
|
||||
userSelector);
|
||||
return this.sessions.getOrCreateSession(sid, org, userSelector);
|
||||
};
|
||||
|
||||
|
||||
@ -230,13 +229,13 @@ Comm.prototype._onWebSocketConnection = async function(websocket, req) {
|
||||
|
||||
// Add a Session object to the client.
|
||||
log.info(`Comm ${client}: using session ${sessionId}`);
|
||||
const loginSession = this.getOrCreateSession(sessionId, req, userSelector);
|
||||
client.setSession(loginSession);
|
||||
const scopedSession = this.getOrCreateSession(sessionId, req, userSelector);
|
||||
client.setSession(scopedSession);
|
||||
|
||||
// Delegate message handling to the client
|
||||
websocket.on('message', client.onMessage.bind(client));
|
||||
|
||||
loginSession.getSessionProfile()
|
||||
scopedSession.getSessionProfile()
|
||||
.then((profile) => {
|
||||
log.debug(`Comm ${client}: sending clientConnect with ` +
|
||||
`${client._missedMessages.length} missed messages`);
|
||||
|
@ -788,15 +788,12 @@ export class FlexServer implements GristServer {
|
||||
this.app.get('/test/login', expressWrap(async (req, res) => {
|
||||
log.warn("Serving unauthenticated /test/login endpoint, made available because GRIST_TEST_LOGIN is set.");
|
||||
|
||||
const session = this.sessions.getOrCreateSessionFromRequest(req);
|
||||
const scopedSession = this.sessions.getOrCreateSessionFromRequest(req);
|
||||
const profile: UserProfile = {
|
||||
email: optStringParam(req.query.email) || 'chimpy@getgrist.com',
|
||||
name: optStringParam(req.query.name) || 'Chimpy McBanana',
|
||||
};
|
||||
await session.scopedSession.operateOnScopedSession(async user => {
|
||||
user.profile = profile;
|
||||
return user;
|
||||
});
|
||||
await scopedSession.updateUserProfile(profile);
|
||||
res.send(`<!doctype html>
|
||||
<html><body>
|
||||
<p>Logged in as ${JSON.stringify(profile)}.<p>
|
||||
@ -811,8 +808,8 @@ export class FlexServer implements GristServer {
|
||||
}
|
||||
|
||||
this.app.get('/logout', expressWrap(async (req, resp) => {
|
||||
const session = this.sessions.getOrCreateSessionFromRequest(req);
|
||||
const userSession = await session.scopedSession.getScopedSession();
|
||||
const scopedSession = this.sessions.getOrCreateSessionFromRequest(req);
|
||||
const userSession = await scopedSession.getScopedSession();
|
||||
|
||||
// If 'next' param is missing, redirect to "/" on our requested hostname.
|
||||
const next = optStringParam(req.query.next) || (req.protocol + '://' + req.get('host') + '/');
|
||||
@ -823,9 +820,7 @@ export class FlexServer implements GristServer {
|
||||
// Express-session will save these changes.
|
||||
const expressSession = (req as any).session;
|
||||
if (expressSession) { expressSession.users = []; expressSession.orgToUser = {}; }
|
||||
if (session.loginSession) {
|
||||
await session.loginSession.clearSession();
|
||||
}
|
||||
await scopedSession.clearScopedSession();
|
||||
resp.redirect(redirectUrl);
|
||||
}));
|
||||
|
||||
|
@ -1,18 +1,19 @@
|
||||
import {HomeDBManager} from 'app/gen-server/lib/HomeDBManager';
|
||||
import {ActiveDoc} from 'app/server/lib/ActiveDoc';
|
||||
import {ScopedSession} from 'app/server/lib/BrowserSession';
|
||||
import * as Comm from 'app/server/lib/Comm';
|
||||
import {DocManager} from 'app/server/lib/DocManager';
|
||||
import {ExternalStorage} from 'app/server/lib/ExternalStorage';
|
||||
import {GristServer} from 'app/server/lib/GristServer';
|
||||
import {IBilling} from 'app/server/lib/IBilling';
|
||||
import {ILoginSession} from 'app/server/lib/ILoginSession';
|
||||
import {INotifier} from 'app/server/lib/INotifier';
|
||||
import {ISandbox, ISandboxCreationOptions} from 'app/server/lib/ISandbox';
|
||||
import {IShell} from 'app/server/lib/IShell';
|
||||
|
||||
export interface ICreate {
|
||||
LoginSession(comm: Comm, sid: string, domain: string, scopeSession: ScopedSession): ILoginSession;
|
||||
// A ScopedSession knows which user is logged in to an org. This method may be used to replace
|
||||
// its behavior with stubs when logins aren't available.
|
||||
adjustSession(scopedSession: ScopedSession): void;
|
||||
|
||||
Billing(dbManager: HomeDBManager, gristConfig: GristServer): IBilling;
|
||||
Notifier(dbManager: HomeDBManager, gristConfig: GristServer): INotifier;
|
||||
Shell(): IShell|undefined;
|
||||
|
@ -1,13 +0,0 @@
|
||||
import {UserProfile} from 'app/common/LoginSessionAPI';
|
||||
import {Client} from 'app/server/lib/Client';
|
||||
|
||||
export interface ILoginSession {
|
||||
clients: Set<Client>;
|
||||
getEmail(): Promise<string>;
|
||||
getSessionProfile(): Promise<UserProfile|null>;
|
||||
// Log out
|
||||
clearSession(): Promise<void>;
|
||||
|
||||
// For testing only. If no email address, profile is wiped, otherwise it is set.
|
||||
testSetProfile(profile: UserProfile|null): Promise<void>;
|
||||
}
|
@ -1,17 +1,10 @@
|
||||
import {ScopedSession} from 'app/server/lib/BrowserSession';
|
||||
import * as Comm from 'app/server/lib/Comm';
|
||||
import {GristServer} from 'app/server/lib/GristServer';
|
||||
import {cookieName, SessionStore} from 'app/server/lib/gristSessions';
|
||||
import {ILoginSession} from 'app/server/lib/ILoginSession';
|
||||
import * as cookie from 'cookie';
|
||||
import * as cookieParser from 'cookie-parser';
|
||||
import {Request} from 'express';
|
||||
|
||||
interface Session {
|
||||
scopedSession: ScopedSession;
|
||||
loginSession?: ILoginSession;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* A collection of all the sessions relevant to this instance of Grist.
|
||||
@ -21,8 +14,7 @@ interface Session {
|
||||
* from code related to websockets.
|
||||
*
|
||||
* The collection caches all existing interfaces to sessions.
|
||||
* LoginSessions play an important role in standalone Grist and address
|
||||
* end-to-end sharing concerns. ScopedSessions play an important role in
|
||||
* ScopedSessions play an important role in
|
||||
* hosted Grist and address per-organization scoping of identity.
|
||||
*
|
||||
* TODO: now this is separated out, we could refactor to share sessions
|
||||
@ -32,7 +24,7 @@ interface Session {
|
||||
*
|
||||
*/
|
||||
export class Sessions {
|
||||
private _sessions = new Map<string, Session>();
|
||||
private _sessions = new Map<string, ScopedSession>();
|
||||
|
||||
constructor(private _sessionSecret: string, private _sessionStore: SessionStore, private _server: GristServer) {
|
||||
}
|
||||
@ -41,7 +33,7 @@ export class Sessions {
|
||||
* Get the session id and organization from the request, and return the
|
||||
* identified session.
|
||||
*/
|
||||
public getOrCreateSessionFromRequest(req: Request): Session {
|
||||
public getOrCreateSessionFromRequest(req: Request): ScopedSession {
|
||||
const sid = this.getSessionIdFromRequest(req);
|
||||
const org = (req as any).org;
|
||||
if (!sid) { throw new Error("session not found"); }
|
||||
@ -51,29 +43,16 @@ export class Sessions {
|
||||
/**
|
||||
* Get or create a session given the session id and organization name.
|
||||
*/
|
||||
public getOrCreateSession(sid: string, domain: string, userSelector: string): Session {
|
||||
public getOrCreateSession(sid: string, domain: string, userSelector: string): ScopedSession {
|
||||
const key = this._getSessionOrgKey(sid, domain, userSelector);
|
||||
if (!this._sessions.has(key)) {
|
||||
const scopedSession = new ScopedSession(sid, this._sessionStore, domain, userSelector);
|
||||
this._sessions.set(key, {scopedSession});
|
||||
this._server.create.adjustSession(scopedSession);
|
||||
this._sessions.set(key, scopedSession);
|
||||
}
|
||||
return this._sessions.get(key)!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access a LoginSession interface, creating it if necessary. For creation,
|
||||
* purposes, Comm, and optionally InstanceManager objects are needed.
|
||||
*
|
||||
*/
|
||||
public getOrCreateLoginSession(sid: string, domain: string, comm: Comm,
|
||||
userSelector: string): ILoginSession {
|
||||
const sess = this.getOrCreateSession(sid, domain, userSelector);
|
||||
if (!sess.loginSession) {
|
||||
sess.loginSession = this._server.create.LoginSession(comm, sid, domain, sess.scopedSession);
|
||||
}
|
||||
return sess.loginSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sessionId from the signed grist cookie.
|
||||
*/
|
||||
|
@ -73,8 +73,8 @@ export class TestingHooks implements ITestingHooks {
|
||||
public async setLoginSessionProfile(gristSidCookie: string, profile: UserProfile|null, org?: string): Promise<void> {
|
||||
log.info("TestingHooks.setLoginSessionProfile called with", gristSidCookie, profile, org);
|
||||
const sessionId = this._comm.getSessionIdFromCookie(gristSidCookie);
|
||||
const loginSession = this._comm.getOrCreateSession(sessionId, {org});
|
||||
return await loginSession.testSetProfile(profile);
|
||||
const scopedSession = this._comm.getOrCreateSession(sessionId, {org});
|
||||
return await scopedSession.updateUserProfile(profile);
|
||||
}
|
||||
|
||||
public async setServerVersion(version: string|null): Promise<void> {
|
||||
|
@ -52,7 +52,7 @@
|
||||
"chai": "4.2.0",
|
||||
"chai-as-promised": "7.1.1",
|
||||
"mocha": "5.2.0",
|
||||
"mocha-webdriver": "0.2.8",
|
||||
"mocha-webdriver": "0.2.9",
|
||||
"moment-locales-webpack-plugin": "^1.2.0",
|
||||
"nodemon": "^2.0.4",
|
||||
"selenium-webdriver": "3.6.0",
|
||||
|
@ -1,22 +0,0 @@
|
||||
import {UserProfile} from 'app/common/LoginSessionAPI';
|
||||
import {Client} from 'app/server/lib/Client';
|
||||
import {ILoginSession} from 'app/server/lib/ILoginSession';
|
||||
|
||||
export class LoginSession implements ILoginSession {
|
||||
public clients: Set<Client> = new Set();
|
||||
public async getEmail() {
|
||||
return process.env.GRIST_DEFAULT_EMAIL || 'anon@getgrist.com';
|
||||
}
|
||||
public async getSessionProfile() {
|
||||
return {
|
||||
name: await this.getEmail(),
|
||||
email: await this.getEmail(),
|
||||
};
|
||||
}
|
||||
public async clearSession(): Promise<void> {
|
||||
// do nothing
|
||||
}
|
||||
public async testSetProfile(profile: UserProfile|null): Promise<void> {
|
||||
// do nothing
|
||||
}
|
||||
}
|
@ -1,14 +1,16 @@
|
||||
import {ActiveDoc} from 'app/server/lib/ActiveDoc';
|
||||
import {ICreate} from 'app/server/lib/ICreate';
|
||||
import {LoginSession} from 'app/server/lib/LoginSession';
|
||||
import {ScopedSession} from 'app/server/lib/BrowserSession';
|
||||
import {NSandboxCreator} from 'app/server/lib/NSandbox';
|
||||
|
||||
// Use raw python - update when pynbox or other solution is set up for core.
|
||||
const sandboxCreator = new NSandboxCreator('unsandboxed');
|
||||
|
||||
export const create: ICreate = {
|
||||
LoginSession() {
|
||||
return new LoginSession();
|
||||
adjustSession(scopedSession: ScopedSession): void {
|
||||
const email = process.env.GRIST_DEFAULT_EMAIL || 'anon@getgrist.com';
|
||||
const profile = {email, name: email};
|
||||
scopedSession.getSessionProfile = async () => profile;
|
||||
},
|
||||
Billing() {
|
||||
return {
|
||||
|
43
yarn.lock
43
yarn.lock
@ -2471,7 +2471,7 @@ fast-safe-stringify@^2.0.7:
|
||||
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
|
||||
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
|
||||
|
||||
fd-slicer@1.1.0, fd-slicer@~1.1.0:
|
||||
fd-slicer@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
|
||||
integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=
|
||||
@ -3135,14 +3135,14 @@ http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3:
|
||||
setprototypeof "1.1.0"
|
||||
statuses ">= 1.4.0 < 2"
|
||||
|
||||
http-errors@~1.7.0:
|
||||
version "1.7.3"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
|
||||
integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
|
||||
http-errors@~1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507"
|
||||
integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==
|
||||
dependencies:
|
||||
depd "~1.1.2"
|
||||
inherits "2.0.4"
|
||||
setprototypeof "1.1.1"
|
||||
setprototypeof "1.2.0"
|
||||
statuses ">= 1.5.0 < 2"
|
||||
toidentifier "1.0.0"
|
||||
|
||||
@ -4231,10 +4231,10 @@ mkdirp@^1.0.3:
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
|
||||
mocha-webdriver@0.2.8:
|
||||
version "0.2.8"
|
||||
resolved "https://registry.yarnpkg.com/mocha-webdriver/-/mocha-webdriver-0.2.8.tgz#d9d0b75476abbff5eb08fe18dd81cfab3addfba5"
|
||||
integrity sha512-1yK7vM7JxNCb3aWLrJRqLQq2Owz/ldKrmoIACJtNHMr1z/v60oi0LKscP8Bc8iSbYTqBmi7c9T6tG5IjQLk9zg==
|
||||
mocha-webdriver@0.2.9:
|
||||
version "0.2.9"
|
||||
resolved "https://registry.yarnpkg.com/mocha-webdriver/-/mocha-webdriver-0.2.9.tgz#1393aa57eb732fd93495064c90b4f9283b601065"
|
||||
integrity sha512-WNr7Yq1uhcvLYyjDChKvW9y2hD8DnzJ0Av/LheZtiS0/6MQtPvxx1XO7OtdnxiS6my9gKg4kRe0944dggLGEFg==
|
||||
dependencies:
|
||||
chai "^4.1.2"
|
||||
chai-as-promised "^7.1.1"
|
||||
@ -4380,14 +4380,13 @@ ms@2.1.2, ms@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
multiparty@4.2.1:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/multiparty/-/multiparty-4.2.1.tgz#d9b6c46d8b8deab1ee70c734b0af771dd46e0b13"
|
||||
integrity sha512-AvESCnNoQlZiOfP9R4mxN8M9csy2L16EIbWIkt3l4FuGti9kXBS8QVzlfyg4HEnarJhrzZilgNFlZtqmoiAIIA==
|
||||
multiparty@4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/multiparty/-/multiparty-4.2.2.tgz#bee5fb5737247628d39dab4979ffd6d57bf60ef6"
|
||||
integrity sha512-NtZLjlvsjcoGrzojtwQwn/Tm90aWJ6XXtPppYF4WmOk/6ncdwMMKggFY2NlRRN9yiCEIVxpOfPWahVEG2HAG8Q==
|
||||
dependencies:
|
||||
fd-slicer "1.1.0"
|
||||
http-errors "~1.7.0"
|
||||
safe-buffer "5.1.2"
|
||||
http-errors "~1.8.0"
|
||||
safe-buffer "5.2.1"
|
||||
uid-safe "2.1.5"
|
||||
|
||||
mz@^2.4.0:
|
||||
@ -5590,7 +5589,7 @@ safe-buffer@5.2.0:
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
|
||||
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
|
||||
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0:
|
||||
safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
@ -5724,10 +5723,10 @@ setprototypeof@1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
|
||||
integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==
|
||||
|
||||
setprototypeof@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
|
||||
integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
|
||||
setprototypeof@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
||||
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
|
||||
|
||||
sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4:
|
||||
version "2.4.11"
|
||||
|
Loading…
Reference in New Issue
Block a user