mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-29 20:53:39 -05:00
Merge pull request #3727 from overleaf/ab-ta-unique-referal-id
Generate User referal_id using longer and more complex token to avoid duplicates GitOrigin-RevId: 302515b0250fec875dcb7b3a505c1c7be4189e2b
This commit is contained in:
parent
7a36373a0f
commit
65d9186e0b
4 changed files with 35 additions and 39 deletions
|
@ -6,7 +6,7 @@ const logger = require('logger-sharelatex')
|
||||||
const TpdsUpdateSender = require('../ThirdPartyDataStore/TpdsUpdateSender')
|
const TpdsUpdateSender = require('../ThirdPartyDataStore/TpdsUpdateSender')
|
||||||
const PublicAccessLevels = require('../Authorization/PublicAccessLevels')
|
const PublicAccessLevels = require('../Authorization/PublicAccessLevels')
|
||||||
const Errors = require('../Errors/Errors')
|
const Errors = require('../Errors/Errors')
|
||||||
const ProjectTokenGenerator = require('./ProjectTokenGenerator')
|
const TokenGenerator = require('../TokenGenerator/TokenGenerator')
|
||||||
const ProjectHelper = require('./ProjectHelper')
|
const ProjectHelper = require('./ProjectHelper')
|
||||||
const settings = require('settings-sharelatex')
|
const settings = require('settings-sharelatex')
|
||||||
const { callbackify } = require('util')
|
const { callbackify } = require('util')
|
||||||
|
@ -236,11 +236,11 @@ async function _generateTokens(project, callback) {
|
||||||
}
|
}
|
||||||
const { tokens } = project
|
const { tokens } = project
|
||||||
if (tokens.readAndWrite == null) {
|
if (tokens.readAndWrite == null) {
|
||||||
const { token, numericPrefix } = ProjectTokenGenerator.readAndWriteToken()
|
const { token, numericPrefix } = TokenGenerator.readAndWriteToken()
|
||||||
tokens.readAndWrite = token
|
tokens.readAndWrite = token
|
||||||
tokens.readAndWritePrefix = numericPrefix
|
tokens.readAndWritePrefix = numericPrefix
|
||||||
}
|
}
|
||||||
if (tokens.readOnly == null) {
|
if (tokens.readOnly == null) {
|
||||||
tokens.readOnly = await ProjectTokenGenerator.promises.generateUniqueReadOnlyToken()
|
tokens.readOnly = await TokenGenerator.promises.generateUniqueReadOnlyToken()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,19 +16,21 @@ const Features = require('../../infrastructure/Features')
|
||||||
const Async = require('async')
|
const Async = require('async')
|
||||||
const { promisify } = require('util')
|
const { promisify } = require('util')
|
||||||
|
|
||||||
|
// (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).
|
||||||
|
const TOKEN_LOWERCASE_ALPHA = 'bcdfghjkmnpqrstvwxyz'
|
||||||
|
const TOKEN_NUMERICS = '123456789'
|
||||||
|
const TOKEN_ALPHANUMERICS =
|
||||||
|
TOKEN_LOWERCASE_ALPHA + TOKEN_LOWERCASE_ALPHA.toUpperCase() + TOKEN_NUMERICS
|
||||||
|
|
||||||
// This module mirrors the token generation in Overleaf (`random_token.rb`),
|
// This module mirrors the token generation in Overleaf (`random_token.rb`),
|
||||||
// for the purposes of implementing token-based project access, like the
|
// for the purposes of implementing token-based project access, like the
|
||||||
// 'unlisted-projects' feature in Overleaf
|
// 'unlisted-projects' feature in Overleaf
|
||||||
|
|
||||||
const ProjectTokenGenerator = {
|
const TokenGenerator = {
|
||||||
// (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) {
|
_randomString(length, alphabet) {
|
||||||
const result = crypto
|
const result = crypto
|
||||||
.randomBytes(length)
|
.randomBytes(length)
|
||||||
|
@ -38,30 +40,25 @@ const ProjectTokenGenerator = {
|
||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
|
|
||||||
// Generate a 12-char token with only characters from TOKEN_ALPHA,
|
// Generate a 12-char token with only characters from TOKEN_LOWERCASE_ALPHA,
|
||||||
// suitable for use as a read-only token for a project
|
// suitable for use as a read-only token for a project
|
||||||
readOnlyToken() {
|
readOnlyToken() {
|
||||||
return ProjectTokenGenerator._randomString(
|
return TokenGenerator._randomString(12, TOKEN_LOWERCASE_ALPHA)
|
||||||
12,
|
|
||||||
ProjectTokenGenerator.TOKEN_ALPHA
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Generate a longer token, with a numeric prefix,
|
// Generate a longer token, with a numeric prefix,
|
||||||
// suitable for use as a read-and-write token for a project
|
// suitable for use as a read-and-write token for a project
|
||||||
readAndWriteToken() {
|
readAndWriteToken() {
|
||||||
const numerics = ProjectTokenGenerator._randomString(
|
const numerics = TokenGenerator._randomString(10, TOKEN_NUMERICS)
|
||||||
10,
|
const token = TokenGenerator._randomString(12, TOKEN_LOWERCASE_ALPHA)
|
||||||
ProjectTokenGenerator.TOKEN_NUMERICS
|
|
||||||
)
|
|
||||||
const token = ProjectTokenGenerator._randomString(
|
|
||||||
12,
|
|
||||||
ProjectTokenGenerator.TOKEN_ALPHA
|
|
||||||
)
|
|
||||||
const fullToken = `${numerics}${token}`
|
const fullToken = `${numerics}${token}`
|
||||||
return { token: fullToken, numericPrefix: numerics }
|
return { token: fullToken, numericPrefix: numerics }
|
||||||
},
|
},
|
||||||
|
|
||||||
|
generateReferralId() {
|
||||||
|
return TokenGenerator._randomString(16, TOKEN_ALPHANUMERICS)
|
||||||
|
},
|
||||||
|
|
||||||
generateUniqueReadOnlyToken(callback) {
|
generateUniqueReadOnlyToken(callback) {
|
||||||
if (callback == null) {
|
if (callback == null) {
|
||||||
callback = function(err, token) {}
|
callback = function(err, token) {}
|
||||||
|
@ -69,7 +66,7 @@ const ProjectTokenGenerator = {
|
||||||
return Async.retry(
|
return Async.retry(
|
||||||
10,
|
10,
|
||||||
function(cb) {
|
function(cb) {
|
||||||
const token = ProjectTokenGenerator.readOnlyToken()
|
const token = TokenGenerator.readOnlyToken()
|
||||||
|
|
||||||
if (!Features.hasFeature('overleaf-integration')) {
|
if (!Features.hasFeature('overleaf-integration')) {
|
||||||
return cb(null, token)
|
return cb(null, token)
|
||||||
|
@ -104,9 +101,9 @@ const ProjectTokenGenerator = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectTokenGenerator.promises = {
|
TokenGenerator.promises = {
|
||||||
generateUniqueReadOnlyToken: promisify(
|
generateUniqueReadOnlyToken: promisify(
|
||||||
ProjectTokenGenerator.generateUniqueReadOnlyToken
|
TokenGenerator.generateUniqueReadOnlyToken
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
module.exports = ProjectTokenGenerator
|
module.exports = TokenGenerator
|
|
@ -1,6 +1,6 @@
|
||||||
const Settings = require('settings-sharelatex')
|
const Settings = require('settings-sharelatex')
|
||||||
const mongoose = require('../infrastructure/Mongoose')
|
const mongoose = require('../infrastructure/Mongoose')
|
||||||
const uuid = require('uuid')
|
const TokenGenerator = require('../Features/TokenGenerator/TokenGenerator')
|
||||||
const { Schema } = mongoose
|
const { Schema } = mongoose
|
||||||
const { ObjectId } = Schema
|
const { ObjectId } = Schema
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ const UserSchema = new Schema({
|
||||||
referal_id: {
|
referal_id: {
|
||||||
type: String,
|
type: String,
|
||||||
default() {
|
default() {
|
||||||
return uuid.v4().split('-')[0]
|
return TokenGenerator.generateReferralId()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
refered_users: [{ type: ObjectId, ref: 'User' }],
|
refered_users: [{ type: ObjectId, ref: 'User' }],
|
||||||
|
|
|
@ -56,7 +56,7 @@ describe('ProjectDetailsHandler', function() {
|
||||||
moveEntity: sinon.stub().resolves()
|
moveEntity: sinon.stub().resolves()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.ProjectTokenGenerator = {
|
this.TokenGenerator = {
|
||||||
readAndWriteToken: sinon.stub(),
|
readAndWriteToken: sinon.stub(),
|
||||||
promises: {
|
promises: {
|
||||||
generateUniqueReadOnlyToken: sinon.stub()
|
generateUniqueReadOnlyToken: sinon.stub()
|
||||||
|
@ -82,7 +82,7 @@ describe('ProjectDetailsHandler', function() {
|
||||||
warn() {},
|
warn() {},
|
||||||
err() {}
|
err() {}
|
||||||
},
|
},
|
||||||
'./ProjectTokenGenerator': this.ProjectTokenGenerator,
|
'../TokenGenerator/TokenGenerator': this.TokenGenerator,
|
||||||
'settings-sharelatex': this.settings
|
'settings-sharelatex': this.settings
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -484,10 +484,10 @@ describe('ProjectDetailsHandler', function() {
|
||||||
this.readOnlyToken = 'abc'
|
this.readOnlyToken = 'abc'
|
||||||
this.readAndWriteToken = '42def'
|
this.readAndWriteToken = '42def'
|
||||||
this.readAndWriteTokenPrefix = '42'
|
this.readAndWriteTokenPrefix = '42'
|
||||||
this.ProjectTokenGenerator.promises.generateUniqueReadOnlyToken.resolves(
|
this.TokenGenerator.promises.generateUniqueReadOnlyToken.resolves(
|
||||||
this.readOnlyToken
|
this.readOnlyToken
|
||||||
)
|
)
|
||||||
this.ProjectTokenGenerator.readAndWriteToken.returns({
|
this.TokenGenerator.readAndWriteToken.returns({
|
||||||
token: this.readAndWriteToken,
|
token: this.readAndWriteToken,
|
||||||
numericPrefix: this.readAndWriteTokenPrefix
|
numericPrefix: this.readAndWriteTokenPrefix
|
||||||
})
|
})
|
||||||
|
@ -506,10 +506,9 @@ describe('ProjectDetailsHandler', function() {
|
||||||
|
|
||||||
it('should update the project with new tokens', async function() {
|
it('should update the project with new tokens', async function() {
|
||||||
await this.handler.promises.ensureTokensArePresent(this.project._id)
|
await this.handler.promises.ensureTokensArePresent(this.project._id)
|
||||||
expect(this.ProjectTokenGenerator.promises.generateUniqueReadOnlyToken)
|
expect(this.TokenGenerator.promises.generateUniqueReadOnlyToken).to.have
|
||||||
.to.have.been.calledOnce
|
.been.calledOnce
|
||||||
expect(this.ProjectTokenGenerator.readAndWriteToken).to.have.been
|
expect(this.TokenGenerator.readAndWriteToken).to.have.been.calledOnce
|
||||||
.calledOnce
|
|
||||||
expect(this.ProjectModel.updateOne).to.have.been.calledOnce
|
expect(this.ProjectModel.updateOne).to.have.been.calledOnce
|
||||||
expect(this.ProjectModel.updateOne).to.have.been.calledWith(
|
expect(this.ProjectModel.updateOne).to.have.been.calledWith(
|
||||||
{ _id: this.project._id },
|
{ _id: this.project._id },
|
||||||
|
|
Loading…
Reference in a new issue