|
|
@@ -1,20 +1,7 @@
|
|
|
-/* eslint-disable
|
|
|
- n/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')
|
|
|
+const { callbackify } = require('util')
|
|
|
|
|
|
// (From Overleaf `random_token.rb`)
|
|
|
// Letters (not numbers! see generate_token) used in tokens. They're all
|
|
|
@@ -30,80 +17,81 @@ const TOKEN_ALPHANUMERICS =
|
|
|
// for the purposes of implementing token-based project access, like the
|
|
|
// 'unlisted-projects' feature in Overleaf
|
|
|
|
|
|
-const TokenGenerator = {
|
|
|
- _randomString(length, alphabet) {
|
|
|
- const result = crypto
|
|
|
- .randomBytes(length)
|
|
|
- .toJSON()
|
|
|
- .data.map(b => alphabet[b % alphabet.length])
|
|
|
- .join('')
|
|
|
- return result
|
|
|
- },
|
|
|
+function _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_LOWERCASE_ALPHA,
|
|
|
+// suitable for use as a read-only token for a project
|
|
|
+function readOnlyToken() {
|
|
|
+ return _randomString(12, TOKEN_LOWERCASE_ALPHA)
|
|
|
+}
|
|
|
|
|
|
- // Generate a 12-char token with only characters from TOKEN_LOWERCASE_ALPHA,
|
|
|
- // suitable for use as a read-only token for a project
|
|
|
- readOnlyToken() {
|
|
|
- return TokenGenerator._randomString(12, TOKEN_LOWERCASE_ALPHA)
|
|
|
- },
|
|
|
+// Generate a longer token, with a numeric prefix,
|
|
|
+// suitable for use as a read-and-write token for a project
|
|
|
+function readAndWriteToken() {
|
|
|
+ const numerics = _randomString(10, TOKEN_NUMERICS)
|
|
|
+ const token = _randomString(12, TOKEN_LOWERCASE_ALPHA)
|
|
|
+ const fullToken = `${numerics}${token}`
|
|
|
+ return { token: fullToken, numericPrefix: numerics }
|
|
|
+}
|
|
|
|
|
|
- // Generate a longer token, with a numeric prefix,
|
|
|
- // suitable for use as a read-and-write token for a project
|
|
|
- readAndWriteToken() {
|
|
|
- const numerics = TokenGenerator._randomString(10, TOKEN_NUMERICS)
|
|
|
- const token = TokenGenerator._randomString(12, TOKEN_LOWERCASE_ALPHA)
|
|
|
- const fullToken = `${numerics}${token}`
|
|
|
- return { token: fullToken, numericPrefix: numerics }
|
|
|
- },
|
|
|
+function generateReferralId() {
|
|
|
+ return _randomString(16, TOKEN_ALPHANUMERICS)
|
|
|
+}
|
|
|
|
|
|
- generateReferralId() {
|
|
|
- return TokenGenerator._randomString(16, TOKEN_ALPHANUMERICS)
|
|
|
- },
|
|
|
+async function generateUniqueReadOnlyToken() {
|
|
|
+ const retryOperation = async () => {
|
|
|
+ const token = readOnlyToken()
|
|
|
|
|
|
- generateUniqueReadOnlyToken(callback) {
|
|
|
- if (callback == null) {
|
|
|
- callback = function () {}
|
|
|
+ if (!Features.hasFeature('saas')) {
|
|
|
+ return token
|
|
|
}
|
|
|
- return Async.retry(
|
|
|
- 10,
|
|
|
- function (cb) {
|
|
|
- const token = TokenGenerator.readOnlyToken()
|
|
|
|
|
|
- if (!Features.hasFeature('saas')) {
|
|
|
- return cb(null, token)
|
|
|
- }
|
|
|
+ const { response, body } = await V1Api.promises.request({
|
|
|
+ url: `/api/v1/overleaf/docs/read_token/${token}/exists`,
|
|
|
+ json: true,
|
|
|
+ })
|
|
|
+
|
|
|
+ if (response.statusCode !== 200) {
|
|
|
+ throw new Error(
|
|
|
+ `non-200 response from v1 read-token-exists api: ${response.statusCode}`
|
|
|
+ )
|
|
|
+ }
|
|
|
|
|
|
- return V1Api.request(
|
|
|
- {
|
|
|
- url: `/api/v1/overleaf/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
|
|
|
- )
|
|
|
- },
|
|
|
+ if (body.exists === true) {
|
|
|
+ throw new Error(`token already exists in v1: ${token}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ return token
|
|
|
+ }
|
|
|
+
|
|
|
+ const MAX_RETRIES = 10
|
|
|
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
|
+ try {
|
|
|
+ return await retryOperation()
|
|
|
+ } catch (err) {
|
|
|
+ if (attempt === MAX_RETRIES) {
|
|
|
+ throw err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const TokenGenerator = {
|
|
|
+ _randomString,
|
|
|
+ readOnlyToken,
|
|
|
+ readAndWriteToken,
|
|
|
+ generateReferralId,
|
|
|
+ generateUniqueReadOnlyToken: callbackify(generateUniqueReadOnlyToken),
|
|
|
}
|
|
|
|
|
|
TokenGenerator.promises = {
|
|
|
- generateUniqueReadOnlyToken: promisify(
|
|
|
- TokenGenerator.generateUniqueReadOnlyToken
|
|
|
- ),
|
|
|
+ generateUniqueReadOnlyToken,
|
|
|
}
|
|
|
module.exports = TokenGenerator
|