mirror of
				https://github.com/gristlabs/grist-core.git
				synced 2025-06-13 20:53:59 +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