mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 17:19:37 +02:00
103 lines
4.9 KiB
JavaScript
103 lines
4.9 KiB
JavaScript
import logger from '@overleaf/logger'
|
|
import passport from 'passport'
|
|
import Settings from '@overleaf/settings'
|
|
import { readFilesContentFromEnv, numFromEnv, boolFromEnv } from '../../../utils.mjs'
|
|
import PermissionsManager from '../../../../../app/src/Features/Authorization/PermissionsManager.mjs'
|
|
import SAMLAuthenticationController from './SAMLAuthenticationController.mjs'
|
|
import { Strategy as SAMLStrategy } from '@node-saml/passport-saml'
|
|
|
|
const SAMLModuleManager = {
|
|
initSettings() {
|
|
Settings.saml = {
|
|
enable: true,
|
|
identityServiceName: process.env.OVERLEAF_SAML_IDENTITY_SERVICE_NAME || 'Log in with SAML IdP',
|
|
attUserId: process.env.OVERLEAF_SAML_USER_ID_FIELD || 'nameID',
|
|
attEmail: process.env.OVERLEAF_SAML_EMAIL_FIELD || 'nameID',
|
|
attFirstName: process.env.OVERLEAF_SAML_FIRST_NAME_FIELD || 'givenName',
|
|
attLastName: process.env.OVERLEAF_SAML_LAST_NAME_FIELD || 'lastName',
|
|
attAdmin: process.env.OVERLEAF_SAML_IS_ADMIN_FIELD,
|
|
valAdmin: process.env.OVERLEAF_SAML_IS_ADMIN_FIELD_VALUE,
|
|
updateUserDetailsOnLogin: boolFromEnv(process.env.OVERLEAF_SAML_UPDATE_USER_DETAILS_ON_LOGIN),
|
|
}
|
|
},
|
|
passportSetup(passport, callback) {
|
|
const samlOptions = {
|
|
entryPoint: process.env.OVERLEAF_SAML_ENTRYPOINT,
|
|
callbackUrl: `${Settings.siteUrl.replace(/\/+$/, '')}/saml/login/callback`,
|
|
issuer: process.env.OVERLEAF_SAML_ISSUER,
|
|
audience: process.env.OVERLEAF_SAML_AUDIENCE,
|
|
idpCert: readFilesContentFromEnv(process.env.OVERLEAF_SAML_IDP_CERT),
|
|
privateKey: readFilesContentFromEnv(process.env.OVERLEAF_SAML_PRIVATE_KEY),
|
|
decryptionPvk: readFilesContentFromEnv(process.env.OVERLEAF_SAML_DECRYPTION_PVK),
|
|
signatureAlgorithm: process.env.OVERLEAF_SAML_SIGNATURE_ALGORITHM,
|
|
additionalParams: JSON.parse(process.env.OVERLEAF_SAML_ADDITIONAL_PARAMS || '{}'),
|
|
additionalAuthorizeParams: JSON.parse(process.env.OVERLEAF_SAML_ADDITIONAL_AUTHORIZE_PARAMS || '{}'),
|
|
identifierFormat: process.env.OVERLEAF_SAML_IDENTIFIER_FORMAT,
|
|
acceptedClockSkewMs: numFromEnv(process.env.OVERLEAF_SAML_ACCEPTED_CLOCK_SKEW_MS),
|
|
attributeConsumingServiceIndex: process.env.OVERLEAF_SAML_ATTRIBUTE_CONSUMING_SERVICE_INDEX,
|
|
authnContext: process.env.OVERLEAF_SAML_AUTHN_CONTEXT ? JSON.parse(process.env.OVERLEAF_SAML_AUTHN_CONTEXT) : undefined,
|
|
forceAuthn: boolFromEnv(process.env.OVERLEAF_SAML_FORCE_AUTHN),
|
|
disableRequestedAuthnContext: boolFromEnv(process.env.OVERLEAF_SAML_DISABLE_REQUESTED_AUTHN_CONTEXT),
|
|
skipRequestCompression: process.env.OVERLEAF_SAML_AUTHN_REQUEST_BINDING === 'HTTP-POST', // compression should be skipped iff authnRequestBinding is POST
|
|
authnRequestBinding: process.env.OVERLEAF_SAML_AUTHN_REQUEST_BINDING,
|
|
validateInResponseTo: process.env.OVERLEAF_SAML_VALIDATE_IN_RESPONSE_TO,
|
|
requestIdExpirationPeriodMs: numFromEnv(process.env.OVERLEAF_SAML_REQUEST_ID_EXPIRATION_PERIOD_MS),
|
|
// cacheProvider: process.env.OVERLEAF_SAML_CACHE_PROVIDER,
|
|
logoutUrl: process.env.OVERLEAF_SAML_LOGOUT_URL,
|
|
logoutCallbackUrl: `${Settings.siteUrl.replace(/\/+$/, '')}/saml/logout/callback`,
|
|
additionalLogoutParams: JSON.parse(process.env.OVERLEAF_SAML_ADDITIONAL_LOGOUT_PARAMS || '{}'),
|
|
wantAssertionsSigned: boolFromEnv(process.env.OVERLEAF_SAML_WANT_ASSERTIONS_SIGNED),
|
|
wantAuthnResponseSigned: boolFromEnv(process.env.OVERLEAF_SAML_WANT_AUTHN_RESPONSE_SIGNED),
|
|
passReqToCallback: true,
|
|
}
|
|
try {
|
|
passport.use(
|
|
new SAMLStrategy(
|
|
samlOptions,
|
|
SAMLAuthenticationController.doPassportLogin,
|
|
SAMLAuthenticationController.doPassportLogout
|
|
)
|
|
)
|
|
callback(null)
|
|
} catch (error) {
|
|
callback(error)
|
|
}
|
|
},
|
|
initPolicy() {
|
|
try {
|
|
PermissionsManager.registerCapability('change-password', { default : true })
|
|
PermissionsManager.registerCapability('use-ai', { default : false })
|
|
} catch (error) {
|
|
logger.info({}, error.message)
|
|
}
|
|
const samlPolicyValidator = async ({ user, subscription }) => {
|
|
// If user is not logged in, user.externalAuth is undefined,
|
|
// in this case allow to change password if the user has a hashedPassword
|
|
return user.externalAuth === 'saml' || (user.externalAuth === undefined && !user.hashedPassword)
|
|
}
|
|
try {
|
|
PermissionsManager.registerPolicy(
|
|
'samlPolicy',
|
|
{ 'change-password' : false },
|
|
{ validator: samlPolicyValidator }
|
|
)
|
|
} catch (error) {
|
|
logger.info({}, error.message)
|
|
}
|
|
},
|
|
getGroupPolicyForUser(user, callback) {
|
|
PermissionsManager.promises.getUserValidationStatus({
|
|
user,
|
|
groupPolicy : { 'samlPolicy' : true },
|
|
subscription : null
|
|
}).then(userValidationMap => {
|
|
let groupPolicy = Object.fromEntries(userValidationMap)
|
|
callback(null, { groupPolicy })
|
|
}).catch(error => {
|
|
callback(error)
|
|
})
|
|
},
|
|
}
|
|
|
|
export default SAMLModuleManager
|