mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
add thirdPartyIdentifiers to User model and add manager class
GitOrigin-RevId: 5839f1f43237c19efed7f4ce1dfb500da9327f70
This commit is contained in:
parent
07b7566466
commit
8f339603f4
4 changed files with 159 additions and 0 deletions
|
@ -110,6 +110,14 @@ SLInV2Error = (message) ->
|
||||||
return error
|
return error
|
||||||
SLInV2Error.prototype.__proto__ = Error.prototype
|
SLInV2Error.prototype.__proto__ = Error.prototype
|
||||||
|
|
||||||
|
ThirdPartyUserNotFoundError = (message) ->
|
||||||
|
message = "user not found for provider and external id" unless message?
|
||||||
|
error = new Error(message)
|
||||||
|
error.name = "ThirdPartyUserNotFoundError"
|
||||||
|
error.__proto__ = SLInV2Error.prototype
|
||||||
|
return error
|
||||||
|
ThirdPartyUserNotFoundError.prototype.__proto__ = Error.prototype
|
||||||
|
|
||||||
module.exports = Errors =
|
module.exports = Errors =
|
||||||
NotFoundError: NotFoundError
|
NotFoundError: NotFoundError
|
||||||
ForbiddenError: ForbiddenError
|
ForbiddenError: ForbiddenError
|
||||||
|
@ -127,3 +135,4 @@ module.exports = Errors =
|
||||||
AccountMergeError: AccountMergeError
|
AccountMergeError: AccountMergeError
|
||||||
NotInV2Error: NotInV2Error
|
NotInV2Error: NotInV2Error
|
||||||
SLInV2Error: SLInV2Error
|
SLInV2Error: SLInV2Error
|
||||||
|
ThirdPartyUserNotFoundError: ThirdPartyUserNotFoundError
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
Errors = require "../Errors/Errors"
|
||||||
|
User = require("../../models/User").User
|
||||||
|
UserUpdater = require "./UserUpdater"
|
||||||
|
_ = require "lodash"
|
||||||
|
|
||||||
|
module.exports = ThirdPartyIdentityManager =
|
||||||
|
login: (providerId, externalUserId, externalData, callback) ->
|
||||||
|
return callback(new Error "invalid arguments") unless providerId? and externalUserId?
|
||||||
|
externalUserId = externalUserId.toString()
|
||||||
|
providerId = providerId.toString()
|
||||||
|
query =
|
||||||
|
"thirdPartyIdentifiers.externalUserId": externalUserId
|
||||||
|
"thirdPartyIdentifiers.providerId": providerId
|
||||||
|
User.findOne query, (err, user) ->
|
||||||
|
return callback err if err?
|
||||||
|
return callback(new Errors.ThirdPartyUserNotFoundError()) unless user
|
||||||
|
# skip updating data unless passed
|
||||||
|
return callback(null, user) unless externalData
|
||||||
|
# get third party identifier object from array
|
||||||
|
thirdPartyIdentifier = user.thirdPartyIdentifiers.find (tpi) ->
|
||||||
|
tpi.externalUserId == externalUserId and tpi.providerId == providerId
|
||||||
|
# do recursive merge of new data over existing data
|
||||||
|
_.merge(thirdPartyIdentifier.externalData, externalData)
|
||||||
|
# update user
|
||||||
|
update = "thirdPartyIdentifiers.$": thirdPartyIdentifier
|
||||||
|
User.findOneAndUpdate query, update, {new: true}, callback
|
||||||
|
|
||||||
|
# register: () ->
|
||||||
|
# this should be implemented once we move to having v2 as the master
|
||||||
|
# but for now we need to register with v1 then call link once that
|
||||||
|
# is complete
|
||||||
|
|
||||||
|
link: (user_id, providerId, externalUserId, externalData, callback, retry) ->
|
||||||
|
query =
|
||||||
|
_id: user_id
|
||||||
|
"thirdPartyIdentifiers.providerId": $ne: providerId
|
||||||
|
update = $push: thirdPartyIdentifiers:
|
||||||
|
externalUserId: externalUserId
|
||||||
|
externalData: externalData
|
||||||
|
providerId: providerId
|
||||||
|
# add new tpi only if an entry for the provider does not exist
|
||||||
|
UserUpdater.updateUser query, update, (err, res) ->
|
||||||
|
return callback err if err?
|
||||||
|
return callback null, res if res.nModified == 1
|
||||||
|
# if already retried then throw error
|
||||||
|
return callback(new Error "update failed") if retry
|
||||||
|
# attempt to clear existing entry then retry
|
||||||
|
ThirdPartyIdentityManager.unlink user_id, providerId, (err) ->
|
||||||
|
return callback err if err?
|
||||||
|
ThirdPartyIdentityManager.link user_id, providerId, externalUserId, externalData, callback, true
|
||||||
|
|
||||||
|
unlink: (user_id, providerId, callback) ->
|
||||||
|
update = $pull: thirdPartyIdentifiers:
|
||||||
|
providerId: providerId
|
||||||
|
UserUpdater.updateUser user_id, update, callback
|
|
@ -78,6 +78,7 @@ UserSchema = new Schema
|
||||||
accessToken: { type: String }
|
accessToken: { type: String }
|
||||||
refreshToken: { type: String }
|
refreshToken: { type: String }
|
||||||
awareOfV2: { type:Boolean, default: false }
|
awareOfV2: { type:Boolean, default: false }
|
||||||
|
thirdPartyIdentifiers: { type: Array, default: [] }
|
||||||
|
|
||||||
conn = mongoose.createConnection(Settings.mongo.url, {
|
conn = mongoose.createConnection(Settings.mongo.url, {
|
||||||
server: {poolSize: Settings.mongo.poolSize || 10},
|
server: {poolSize: Settings.mongo.poolSize || 10},
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
Errors = require "../../../app/js/Features/Errors/Errors"
|
||||||
|
Settings = require "settings-sharelatex"
|
||||||
|
User = require "./helpers/User"
|
||||||
|
ThirdPartyIdentityManager = require "../../../app/js/Features/User/ThirdPartyIdentityManager"
|
||||||
|
chai = require "chai"
|
||||||
|
|
||||||
|
expect = chai.expect
|
||||||
|
|
||||||
|
describe "ThirdPartyIdentityManager", ->
|
||||||
|
beforeEach (done) ->
|
||||||
|
@provider = "provider"
|
||||||
|
@externalUserId = "external-user-id"
|
||||||
|
@externalData = test: "data"
|
||||||
|
@user = new User()
|
||||||
|
@user.ensureUserExists done
|
||||||
|
|
||||||
|
afterEach (done) ->
|
||||||
|
@user.full_delete_user @user.email, done
|
||||||
|
|
||||||
|
describe "login", ->
|
||||||
|
describe "when third party identity exists", ->
|
||||||
|
beforeEach (done) ->
|
||||||
|
ThirdPartyIdentityManager.link @user.id, @provider, @externalUserId, @externalData, done
|
||||||
|
|
||||||
|
it "should return user", (done) ->
|
||||||
|
ThirdPartyIdentityManager.login @provider, @externalUserId, @externalData, (err, user) =>
|
||||||
|
expect(err).to.be.null
|
||||||
|
expect(user._id.toString()).to.equal @user.id
|
||||||
|
done()
|
||||||
|
return
|
||||||
|
|
||||||
|
it "should merge external data", (done) ->
|
||||||
|
@externalData =
|
||||||
|
test: "different"
|
||||||
|
another: "key"
|
||||||
|
ThirdPartyIdentityManager.login @provider, @externalUserId, @externalData, (err, user) =>
|
||||||
|
expect(err).to.be.null
|
||||||
|
expect(user.thirdPartyIdentifiers[0].externalData).to.deep.equal @externalData
|
||||||
|
done()
|
||||||
|
return
|
||||||
|
|
||||||
|
describe "when third party identity does not exists", ->
|
||||||
|
it "should return error", (done) ->
|
||||||
|
ThirdPartyIdentityManager.login @provider, @externalUserId, @externalData, (err, user) =>
|
||||||
|
expect(err.name).to.equal "ThirdPartyUserNotFoundError"
|
||||||
|
done()
|
||||||
|
return
|
||||||
|
|
||||||
|
describe "link", ->
|
||||||
|
describe "when provider not already linked", ->
|
||||||
|
it "should link provider to user", (done) ->
|
||||||
|
ThirdPartyIdentityManager.link @user.id, @provider, @externalUserId, @externalData, (err, res) ->
|
||||||
|
expect(res.nModified).to.equal 1
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe "when provider is already linked", ->
|
||||||
|
beforeEach (done) ->
|
||||||
|
ThirdPartyIdentityManager.link @user.id, @provider, @externalUserId, @externalData, done
|
||||||
|
|
||||||
|
it "should link provider to user", (done) ->
|
||||||
|
ThirdPartyIdentityManager.link @user.id, @provider, @externalUserId, @externalData, (err, res) ->
|
||||||
|
expect(res.nModified).to.equal 1
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "should not create duplicate thirdPartyIdentifiers", (done) ->
|
||||||
|
ThirdPartyIdentityManager.link @user.id, @provider, @externalUserId, @externalData, (err, res) =>
|
||||||
|
@user.get (err, user) ->
|
||||||
|
expect(user.thirdPartyIdentifiers.length).to.equal 1
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "should replace existing data", (done) ->
|
||||||
|
@externalData = replace: "data"
|
||||||
|
ThirdPartyIdentityManager.link @user.id, @provider, @externalUserId, @externalData, (err, res) =>
|
||||||
|
@user.get (err, user) =>
|
||||||
|
expect(user.thirdPartyIdentifiers[0].externalData).to.deep.equal @externalData
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe "unlink", ->
|
||||||
|
describe "when provider not already linked", ->
|
||||||
|
it "should succeed", (done) ->
|
||||||
|
ThirdPartyIdentityManager.unlink @user.id, @provider, (err, res) ->
|
||||||
|
expect(err).to.be.null
|
||||||
|
expect(res.nModified).to.equal 0
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe "when provider is already linked", ->
|
||||||
|
beforeEach (done) ->
|
||||||
|
ThirdPartyIdentityManager.link @user.id, @provider, @externalUserId, @externalData, done
|
||||||
|
|
||||||
|
it "should remove thirdPartyIdentifiers entry", (done) ->
|
||||||
|
ThirdPartyIdentityManager.unlink @user.id, @provider, (err, res) =>
|
||||||
|
@user.get (err, user) ->
|
||||||
|
expect(user.thirdPartyIdentifiers.length).to.equal 0
|
||||||
|
done()
|
Loading…
Reference in a new issue