mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #19946 from overleaf/ac-remove-ol-int-imports-from-tests
[web] Remove overleaf-integration imports from tests GitOrigin-RevId: 96a3afaab386c486c948d35999f2acf4cedc77cf
This commit is contained in:
parent
0e43a18bc6
commit
a6c97cd506
2 changed files with 265 additions and 0 deletions
219
services/web/test/acceptance/src/helpers/groupSSO.js
Normal file
219
services/web/test/acceptance/src/helpers/groupSSO.js
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
const fs = require('fs')
|
||||||
|
const Path = require('path')
|
||||||
|
const User = require('./User').promises
|
||||||
|
const Subscription = require('./Subscription').promises
|
||||||
|
const { SSOConfig } = require('../../../../app/src/models/SSOConfig')
|
||||||
|
const UserHelper = require('./UserHelper')
|
||||||
|
const SAMLHelper = require('./SAMLHelper')
|
||||||
|
const Settings = require('@overleaf/settings')
|
||||||
|
const {
|
||||||
|
getProviderId,
|
||||||
|
} = require('../../../../app/src/Features/Subscription/GroupUtils')
|
||||||
|
const UserGetter = require('../../../../app/src/Features/User/UserGetter')
|
||||||
|
const {
|
||||||
|
Subscription: SubscriptionModel,
|
||||||
|
} = require('../../../../app/src/models/Subscription')
|
||||||
|
|
||||||
|
const SAML_TEST_CERT = fs
|
||||||
|
.readFileSync(Path.resolve(__dirname, '../../files/saml-cert.crt'), 'utf8')
|
||||||
|
.replace(/-----BEGIN CERTIFICATE-----/, '')
|
||||||
|
.replace(/-----END CERTIFICATE-----/, '')
|
||||||
|
.replace(/\n/g, '')
|
||||||
|
|
||||||
|
function getEnrollmentUrl(groupId) {
|
||||||
|
return `/subscription/${groupId}/sso_enrollment`
|
||||||
|
}
|
||||||
|
|
||||||
|
const userIdAttribute = 'nameID'
|
||||||
|
|
||||||
|
const baseSsoConfig = {
|
||||||
|
entryPoint: 'http://example-sso.com/saml',
|
||||||
|
certificates: [SAML_TEST_CERT],
|
||||||
|
signatureAlgorithm: 'sha256',
|
||||||
|
userIdAttribute,
|
||||||
|
} // the database also sets enabled and validated, but we cannot set that in the POST request for /manage/groups/:ID/settings/sso
|
||||||
|
|
||||||
|
async function createGroupSSO() {
|
||||||
|
const nonSSOMemberHelper = await UserHelper.createUser()
|
||||||
|
const nonSSOMember = nonSSOMemberHelper.user
|
||||||
|
|
||||||
|
const groupAdminUser = new User()
|
||||||
|
const memberUser = new User()
|
||||||
|
|
||||||
|
await groupAdminUser.ensureUserExists()
|
||||||
|
await memberUser.ensureUserExists()
|
||||||
|
|
||||||
|
const ssoConfig = new SSOConfig({
|
||||||
|
...baseSsoConfig,
|
||||||
|
enabled: true,
|
||||||
|
validated: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
await ssoConfig.save()
|
||||||
|
|
||||||
|
const subscription = new Subscription({
|
||||||
|
adminId: groupAdminUser._id,
|
||||||
|
memberIds: [memberUser._id, nonSSOMember._id, groupAdminUser._id],
|
||||||
|
groupPlan: true,
|
||||||
|
planCode: 'group_professional_10_enterprise',
|
||||||
|
features: {
|
||||||
|
groupSSO: true,
|
||||||
|
},
|
||||||
|
ssoConfig: ssoConfig._id,
|
||||||
|
membersLimit: 10,
|
||||||
|
})
|
||||||
|
await subscription.ensureExists()
|
||||||
|
const subscriptionId = subscription._id.toString()
|
||||||
|
const enrollmentUrl = getEnrollmentUrl(subscriptionId)
|
||||||
|
const internalProviderId = getProviderId(subscriptionId)
|
||||||
|
|
||||||
|
await linkGroupMember(
|
||||||
|
memberUser.email,
|
||||||
|
memberUser.password,
|
||||||
|
subscriptionId,
|
||||||
|
'mock@email.com'
|
||||||
|
)
|
||||||
|
|
||||||
|
const userHelper = new UserHelper()
|
||||||
|
|
||||||
|
return {
|
||||||
|
ssoConfig,
|
||||||
|
internalProviderId,
|
||||||
|
userIdAttribute,
|
||||||
|
subscription,
|
||||||
|
subscriptionId,
|
||||||
|
groupAdminUser,
|
||||||
|
memberUser,
|
||||||
|
nonSSOMemberHelper,
|
||||||
|
nonSSOMember,
|
||||||
|
userHelper,
|
||||||
|
enrollmentUrl,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function linkGroupMember(
|
||||||
|
userEmail,
|
||||||
|
userPassword,
|
||||||
|
groupId,
|
||||||
|
externalUserId
|
||||||
|
) {
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
const subscription = await SubscriptionModel.findById(groupId)
|
||||||
|
.populate('ssoConfig')
|
||||||
|
.exec()
|
||||||
|
const userIdAttribute = subscription?.ssoConfig?.userIdAttribute
|
||||||
|
|
||||||
|
const internalProviderId = getProviderId(groupId)
|
||||||
|
const enrollmentUrl = getEnrollmentUrl(groupId)
|
||||||
|
const userHelper = await UserHelper.loginUser(
|
||||||
|
{
|
||||||
|
email: userEmail,
|
||||||
|
password: userPassword,
|
||||||
|
},
|
||||||
|
`/subscription/${groupId}/sso_enrollment`
|
||||||
|
)
|
||||||
|
|
||||||
|
const { headers } = await userHelper.fetch(enrollmentUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
if (
|
||||||
|
!headers.get('location') ||
|
||||||
|
!headers.get('location').includes(Settings.saml.groupSSO.initPath)
|
||||||
|
) {
|
||||||
|
throw new Error('invalid redirect when linking to group SSO')
|
||||||
|
}
|
||||||
|
|
||||||
|
const redirectTo = new URL(headers.get('location'))
|
||||||
|
|
||||||
|
const initSSOResponse = await userHelper.fetch(redirectTo)
|
||||||
|
|
||||||
|
// redirect to IdP
|
||||||
|
const idpEntryPointUrl = new URL(initSSOResponse.headers.get('location'))
|
||||||
|
const requestId = await SAMLHelper.getRequestId(idpEntryPointUrl)
|
||||||
|
const response = await userHelper.fetch(Settings.saml.groupSSO.path, {
|
||||||
|
method: 'POST',
|
||||||
|
body: new URLSearchParams({
|
||||||
|
SAMLResponse: SAMLHelper.createMockSamlResponse({
|
||||||
|
requestId,
|
||||||
|
userIdAttribute,
|
||||||
|
uniqueId: externalUserId,
|
||||||
|
issuer: 'https://www.overleaf.test/saml/group-sso/meta',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
if (response.status !== 302) {
|
||||||
|
throw new Error('failed to link group SSO')
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure user linked
|
||||||
|
const user = await UserGetter.promises.getUser(
|
||||||
|
{ email: userEmail },
|
||||||
|
{ samlIdentifiers: 1, enrollment: 1 }
|
||||||
|
)
|
||||||
|
|
||||||
|
const { enrollment, samlIdentifiers } = user
|
||||||
|
const linkedToGroupSSO = samlIdentifiers.some(
|
||||||
|
identifier => identifier.providerId === internalProviderId
|
||||||
|
)
|
||||||
|
const userIsEnrolledInSSO = enrollment.sso.some(
|
||||||
|
sso => sso.groupId.toString() === groupId.toString()
|
||||||
|
)
|
||||||
|
if (!linkedToGroupSSO || !userIsEnrolledInSSO) {
|
||||||
|
throw new Error('error setting up test user with group SSO linked')
|
||||||
|
}
|
||||||
|
|
||||||
|
return userHelper
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setConfigAndEnableSSO(
|
||||||
|
subscriptionHelper,
|
||||||
|
adminEmailPassword,
|
||||||
|
config
|
||||||
|
) {
|
||||||
|
config = config || {
|
||||||
|
entryPoint: 'http://idp.example.com/entry_point',
|
||||||
|
certificates: [SAML_TEST_CERT],
|
||||||
|
userIdAttribute: 'email',
|
||||||
|
userLastNameAttribute: 'lastName',
|
||||||
|
}
|
||||||
|
|
||||||
|
const { email, password } = adminEmailPassword
|
||||||
|
const userHelper = await UserHelper.loginUser({
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
})
|
||||||
|
|
||||||
|
const createResponse = await userHelper.fetch(
|
||||||
|
`/manage/groups/${subscriptionHelper._id}/settings/sso`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(config),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (createResponse.status !== 201) {
|
||||||
|
throw new Error(
|
||||||
|
`failed to set SSO config. Status = ${createResponse.status}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await subscriptionHelper.setValidatedSSO()
|
||||||
|
|
||||||
|
const enableResponse = await userHelper.fetch(
|
||||||
|
`/manage/groups/${subscriptionHelper._id}/settings/enableSSO`,
|
||||||
|
{ method: 'POST' }
|
||||||
|
)
|
||||||
|
|
||||||
|
if (enableResponse.status !== 200) {
|
||||||
|
throw new Error(`failed to enable SSO. Status = ${enableResponse.status}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
createGroupSSO,
|
||||||
|
linkGroupMember,
|
||||||
|
baseSsoConfig,
|
||||||
|
setConfigAndEnableSSO,
|
||||||
|
}
|
46
services/web/test/acceptance/src/mocks/MockGoogleOauthApi.js
Normal file
46
services/web/test/acceptance/src/mocks/MockGoogleOauthApi.js
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
const AbstractMockApi = require('./AbstractMockApi')
|
||||||
|
|
||||||
|
class MockGoogleOauthApi extends AbstractMockApi {
|
||||||
|
reset() {
|
||||||
|
this.profiles = {}
|
||||||
|
this.tokens = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
addProfile(profile, token, authorizationCode) {
|
||||||
|
this.profiles[token] = {
|
||||||
|
picture: 'https://example.com/picture.jpg',
|
||||||
|
email_verified: true,
|
||||||
|
locale: 'en-GB',
|
||||||
|
...profile,
|
||||||
|
}
|
||||||
|
this.tokens[authorizationCode] = token
|
||||||
|
}
|
||||||
|
|
||||||
|
applyRoutes() {
|
||||||
|
this.app.post('/oauth/token', (req, res, next) => {
|
||||||
|
if (!this.tokens[req.body.code]) {
|
||||||
|
return res.sendStatus(400)
|
||||||
|
}
|
||||||
|
res.json({
|
||||||
|
access_token: this.tokens[req.body.code],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.app.get('/oauth2/v3/userinfo', (req, res, next) => {
|
||||||
|
if (!this.profiles[req.query.access_token]) {
|
||||||
|
return res.sendStatus(400)
|
||||||
|
}
|
||||||
|
res.json(this.profiles[req.query.access_token])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = MockGoogleOauthApi
|
||||||
|
|
||||||
|
// type hint for the inherited `instance` method
|
||||||
|
/**
|
||||||
|
* @function instance
|
||||||
|
* @memberOf MockGoogleOauthApi
|
||||||
|
* @static
|
||||||
|
* @returns {MockGoogleOauthApi}
|
||||||
|
*/
|
Loading…
Reference in a new issue