2018-04-02 20:23:44 +02:00
|
|
|
// Free API to get location from IP: http://freegeoip.net/json/149.11.144.50
|
2018-05-11 10:12:56 +02:00
|
|
|
|
2018-05-05 22:11:43 +02:00
|
|
|
const randomstring = require('random-string')
|
2018-05-04 19:53:16 +02:00
|
|
|
import {sendEmail} from './post_office'
|
|
|
|
import {domain} from './config'
|
2019-01-31 14:18:26 +01:00
|
|
|
import Repository from './repository'
|
2018-05-11 08:24:18 +02:00
|
|
|
import {DBEntity, User} from "./types";
|
2018-05-04 19:53:16 +02:00
|
|
|
|
2018-09-30 12:33:59 +02:00
|
|
|
export class SessionAlreadyValidatedError extends Error {}
|
2018-09-30 09:36:17 +02:00
|
|
|
|
2018-05-13 07:45:26 +02:00
|
|
|
const generateHash = () => randomstring({
|
2018-05-11 10:12:56 +02:00
|
|
|
length: 48,
|
|
|
|
letters: true,
|
|
|
|
special: false
|
|
|
|
})
|
2018-05-04 19:53:16 +02:00
|
|
|
|
2018-10-07 21:33:47 +02:00
|
|
|
export class SessionInvite implements DBEntity {
|
2018-05-11 08:24:18 +02:00
|
|
|
hash: string
|
2018-05-04 19:53:16 +02:00
|
|
|
userId: string
|
2018-05-11 10:12:56 +02:00
|
|
|
timeValidated?: number
|
2018-10-07 21:33:47 +02:00
|
|
|
id: string
|
2018-05-04 19:53:16 +02:00
|
|
|
|
|
|
|
constructor(user: User) {
|
2018-05-11 10:12:56 +02:00
|
|
|
this.hash = generateHash()
|
2018-05-04 19:53:16 +02:00
|
|
|
this.userId = user.id
|
2018-05-11 10:12:56 +02:00
|
|
|
this.timeValidated = null
|
2018-04-02 20:23:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-11 10:12:56 +02:00
|
|
|
interface BasicSessionData {
|
|
|
|
hash: string
|
|
|
|
userId: string
|
|
|
|
}
|
|
|
|
|
|
|
|
export type Session = BasicSessionData & {
|
|
|
|
ctime: number
|
|
|
|
isValid: boolean
|
|
|
|
}
|
|
|
|
|
2018-09-02 11:11:16 +02:00
|
|
|
export type SessionRequest = DBEntity & {
|
2018-05-11 08:24:18 +02:00
|
|
|
hash: string
|
|
|
|
}
|
|
|
|
|
2018-05-04 19:53:16 +02:00
|
|
|
export default class SessionManager {
|
|
|
|
|
2018-05-11 10:12:56 +02:00
|
|
|
repository: Repository
|
2018-05-04 19:53:16 +02:00
|
|
|
|
2018-05-11 10:12:56 +02:00
|
|
|
constructor(repository: Repository) {
|
|
|
|
this.repository = repository
|
|
|
|
}
|
|
|
|
|
2019-01-27 19:07:09 +01:00
|
|
|
inviteUser = async (email: string): Promise<SessionInvite> => {
|
|
|
|
const user = await this.repository.getUser({ email });
|
|
|
|
if (!user) {
|
|
|
|
throw new Error('Could not find user for this email');
|
|
|
|
}
|
2018-05-11 10:12:56 +02:00
|
|
|
const invite = new SessionInvite(user)
|
|
|
|
try {
|
|
|
|
const persistedInvite = await this.repository.saveSessionInvite(invite)
|
|
|
|
console.log(`Invite persisted for user ${user.username}`, persistedInvite)
|
|
|
|
} catch (e) {
|
|
|
|
console.error('Failed to save invite', invite)
|
|
|
|
throw e;
|
2018-05-04 19:53:16 +02:00
|
|
|
}
|
|
|
|
|
2018-05-19 13:43:35 +02:00
|
|
|
return new Promise(async (resolve: (SessionInvite) => void, reject)=>{
|
|
|
|
try {
|
|
|
|
await sendEmail({
|
|
|
|
to: user.email,
|
|
|
|
from: `signin@${domain}`,
|
2018-09-30 12:33:59 +02:00
|
|
|
text: `Follow this link to start a session: https://${domain}/login/${invite.hash}`,
|
2018-05-19 13:43:35 +02:00
|
|
|
subject: 'Invitation for session'
|
|
|
|
})
|
|
|
|
resolve(invite)
|
|
|
|
console.log(`Sent invitation to ${user.email}`)
|
|
|
|
} catch (e) {
|
|
|
|
reject(e)
|
|
|
|
console.error('Failed to send invitation email ', invite, e)
|
|
|
|
throw e
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2018-05-11 10:12:56 +02:00
|
|
|
}
|
2018-05-04 19:53:16 +02:00
|
|
|
|
2019-01-30 16:44:05 +01:00
|
|
|
initiateSession = async (inviteHash: string): Promise<Session> => {
|
|
|
|
const sessionInvite: SessionInvite = await this.repository.getSessionInvite(inviteHash)
|
|
|
|
if (!sessionInvite) {
|
|
|
|
throw new Error(`Could not find invite with hash ${inviteHash}`)
|
|
|
|
}
|
2018-05-11 10:12:56 +02:00
|
|
|
if (sessionInvite.timeValidated) {
|
2018-09-30 09:36:17 +02:00
|
|
|
throw new SessionAlreadyValidatedError()
|
2018-05-11 10:12:56 +02:00
|
|
|
}
|
2018-09-29 15:25:11 +02:00
|
|
|
const matchingUser: User = await this.repository.getUserById(sessionInvite.userId)
|
2019-01-30 16:44:05 +01:00
|
|
|
if (!matchingUser) {
|
|
|
|
console.error(`Invite hash ${inviteHash} could not find related userId ${sessionInvite.userId}`)
|
|
|
|
throw new Error('Cannot find related user to this invite')
|
|
|
|
}
|
2018-05-16 20:48:38 +02:00
|
|
|
if (matchingUser.id === sessionInvite.userId) {
|
2018-05-11 10:12:56 +02:00
|
|
|
const session: Session = {
|
|
|
|
userId: matchingUser.id,
|
|
|
|
ctime: Date.now(),
|
|
|
|
isValid: true,
|
|
|
|
hash: generateHash(),
|
2018-05-04 19:53:16 +02:00
|
|
|
}
|
2018-05-11 10:12:56 +02:00
|
|
|
const persistedSession = await this.repository.createSession(session)
|
|
|
|
return Promise.resolve(persistedSession)
|
|
|
|
} else {
|
2018-05-13 07:45:26 +02:00
|
|
|
console.warn(`user ids didn't match. userId: ${matchingUser.id}. sessionUserId: ${sessionInvite.userId}`)
|
|
|
|
return Promise.resolve(null)
|
2018-05-04 19:53:16 +02:00
|
|
|
}
|
2018-05-11 10:12:56 +02:00
|
|
|
}
|
2018-06-03 12:33:51 +02:00
|
|
|
|
|
|
|
getSession = async(sessionHash: string): Promise<Session> => {
|
|
|
|
return await this.repository.getSession(sessionHash)
|
|
|
|
}
|
2018-05-04 19:53:16 +02:00
|
|
|
}
|
|
|
|
|