overleaf/services/web/app/src/Features/Project/ProjectTokenGenerator.js
Alf Eaton 2ff1cf43d6 Merge pull request #3470 from overleaf/eslint
Upgrade and configure ESLint

GitOrigin-RevId: ad5aeaf85e72c847a125ff3a9db99a12855e38aa
2020-12-16 03:08:28 +00:00

112 lines
3.4 KiB
JavaScript

/* eslint-disable
node/handle-callback-err,
max-len,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const crypto = require('crypto')
const V1Api = require('../V1/V1Api')
const Features = require('../../infrastructure/Features')
const Async = require('async')
const { promisify } = require('util')
// This module mirrors the token generation in Overleaf (`random_token.rb`),
// for the purposes of implementing token-based project access, like the
// 'unlisted-projects' feature in Overleaf
const ProjectTokenGenerator = {
// (From Overleaf `random_token.rb`)
// Letters (not numbers! see generate_token) used in tokens. They're all
// consonants, to avoid embarassing words (I can't think of any that use only
// a y), and lower case "l" is omitted, because in many fonts it is
// indistinguishable from an upper case "I" (and sometimes even the number 1).
TOKEN_ALPHA: 'bcdfghjkmnpqrstvwxyz',
TOKEN_NUMERICS: '123456789',
_randomString(length, alphabet) {
const result = crypto
.randomBytes(length)
.toJSON()
.data.map(b => alphabet[b % alphabet.length])
.join('')
return result
},
// Generate a 12-char token with only characters from TOKEN_ALPHA,
// suitable for use as a read-only token for a project
readOnlyToken() {
return ProjectTokenGenerator._randomString(
12,
ProjectTokenGenerator.TOKEN_ALPHA
)
},
// Generate a longer token, with a numeric prefix,
// suitable for use as a read-and-write token for a project
readAndWriteToken() {
const numerics = ProjectTokenGenerator._randomString(
10,
ProjectTokenGenerator.TOKEN_NUMERICS
)
const token = ProjectTokenGenerator._randomString(
12,
ProjectTokenGenerator.TOKEN_ALPHA
)
const fullToken = `${numerics}${token}`
return { token: fullToken, numericPrefix: numerics }
},
generateUniqueReadOnlyToken(callback) {
if (callback == null) {
callback = function(err, token) {}
}
return Async.retry(
10,
function(cb) {
const token = ProjectTokenGenerator.readOnlyToken()
if (!Features.hasFeature('overleaf-integration')) {
return cb(null, token)
}
return V1Api.request(
{
url: `/api/v1/sharelatex/docs/read_token/${token}/exists`,
json: true
},
function(err, response, body) {
if (err != null) {
return cb(err)
}
if (response.statusCode !== 200) {
return cb(
new Error(
`non-200 response from v1 read-token-exists api: ${response.statusCode}`
)
)
}
if (body.exists === true) {
return cb(new Error(`token already exists in v1: ${token}`))
} else {
return cb(null, token)
}
}
)
},
callback
)
}
}
ProjectTokenGenerator.promises = {
generateUniqueReadOnlyToken: promisify(
ProjectTokenGenerator.generateUniqueReadOnlyToken
)
}
module.exports = ProjectTokenGenerator