mirror of
https://github.com/overleaf/overleaf.git
synced 2024-10-31 21:21:03 -04:00
176ead8983
* added primary-email-check page, route and controllers * add `#add-email` internal link in settings to display new email form * added primary-email-check redirection with split test * update `lastPrimaryEmailCheck` when the default email address is set * added `lastPrimaryCheck` to admin panel * translations for primary-email-check * acceptance tests for primary-email-check * [web] multi-submit for primary email check * Using `confirmedAt` to prevent from displaying primary-email-check page Co-authored-by: Jakob Ackermann <jakob.ackermann@overleaf.com> Co-Authored-By: Miguel Serrano <mserranom@gmail.com> GitOrigin-RevId: d8e3a280439da08038a4487d8bfd7b3b0596e3b5
363 lines
12 KiB
JavaScript
363 lines
12 KiB
JavaScript
import _ from 'lodash'
|
|
/* eslint-disable
|
|
max-len,
|
|
no-return-assign,
|
|
no-useless-escape,
|
|
*/
|
|
|
|
import App from '../../../base'
|
|
import getMeta from '../../../utils/meta'
|
|
|
|
export default App.controller(
|
|
'UserAffiliationsController',
|
|
function ($scope, $rootScope, UserAffiliationsDataService, $q, $window) {
|
|
$scope.userEmails = []
|
|
$scope.linkedInstitutionIds = []
|
|
$scope.hideInstitutionNotifications = {}
|
|
$scope.closeInstitutionNotification = type => {
|
|
$scope.hideInstitutionNotifications[type] = true
|
|
}
|
|
$scope.samlBetaSession = ExposedSettings.hasSamlBeta
|
|
$scope.samlInitPath = ExposedSettings.samlInitPath
|
|
$scope.reconfirmationRemoveEmail = getMeta('ol-reconfirmationRemoveEmail')
|
|
$scope.reconfirmedViaSAML = getMeta('ol-reconfirmedViaSAML')
|
|
|
|
$scope.showInstitutionalLeaversSurvey = false
|
|
$scope.dismissInstitutionalLeaversSurvey = () => {
|
|
try {
|
|
localStorage.setItem('hideInstitutionalLeaversSurvey', true)
|
|
} catch (e) {}
|
|
$scope.showInstitutionalLeaversSurvey = false
|
|
}
|
|
function maybeShowInstitutionalLeaversSurvey() {
|
|
if (localStorage.getItem('hideInstitutionalLeaversSurvey')) return
|
|
if (
|
|
localStorage.getItem('showInstitutionalLeaversSurveyUntil') > Date.now()
|
|
) {
|
|
$scope.showInstitutionalLeaversSurvey = true
|
|
}
|
|
}
|
|
maybeShowInstitutionalLeaversSurvey()
|
|
|
|
const ONE_WEEK_IN_MS = 7 * 24 * 60 * 60 * 1000
|
|
const LOCAL_AND_DOMAIN_REGEX = /([^@]+)@(.+)/
|
|
const EMAIL_REGEX =
|
|
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\ ".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA -Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
|
|
|
const _matchLocalAndDomain = function (userEmailInput) {
|
|
const match = userEmailInput
|
|
? userEmailInput.match(LOCAL_AND_DOMAIN_REGEX)
|
|
: undefined
|
|
if (match) {
|
|
return { local: match[1], domain: match[2] }
|
|
} else {
|
|
return { local: null, domain: null }
|
|
}
|
|
}
|
|
|
|
const _ssoAvailableForAffiliation = affiliation => {
|
|
if (!affiliation) return false
|
|
const institution = affiliation.institution
|
|
if (!_ssoAvailableForInstitution(institution)) return false
|
|
if (!institution.confirmed) return false // domain is confirmed, not the email
|
|
return true
|
|
}
|
|
|
|
const _ssoAvailableForDomain = domain => {
|
|
if (!domain) return false
|
|
if (!domain.confirmed) return false // domain is confirmed, not the email
|
|
const institution = domain.university
|
|
if (!_ssoAvailableForInstitution(institution)) return false
|
|
return true
|
|
}
|
|
|
|
const _ssoAvailableForInstitution = institution => {
|
|
if (!ExposedSettings.hasSamlFeature) return false
|
|
if (!institution) return false
|
|
if (institution.ssoEnabled) return true
|
|
if ($scope.samlBetaSession && institution.ssoBeta) return true
|
|
return false
|
|
}
|
|
|
|
$scope.getEmailSuggestion = function (userInput) {
|
|
const userInputLocalAndDomain = _matchLocalAndDomain(userInput)
|
|
$scope.ui.isValidEmail = EMAIL_REGEX.test(userInput)
|
|
$scope.ui.isBlacklistedEmail = false
|
|
$scope.ui.showManualUniversitySelectionUI = false
|
|
if (userInputLocalAndDomain.domain) {
|
|
$scope.ui.isBlacklistedEmail =
|
|
UserAffiliationsDataService.isDomainBlacklisted(
|
|
userInputLocalAndDomain.domain
|
|
)
|
|
return UserAffiliationsDataService.getUniversityDomainFromPartialDomainInput(
|
|
userInputLocalAndDomain.domain
|
|
)
|
|
.then(function (universityDomain) {
|
|
const currentUserInputLocalAndDomain = _matchLocalAndDomain(
|
|
$scope.newAffiliation.email
|
|
)
|
|
if (
|
|
currentUserInputLocalAndDomain.domain ===
|
|
universityDomain.hostname
|
|
) {
|
|
$scope.newAffiliation.university = universityDomain.university
|
|
$scope.newAffiliation.department = universityDomain.department
|
|
$scope.newAffiliation.ssoAvailable =
|
|
_ssoAvailableForDomain(universityDomain)
|
|
} else {
|
|
_resetAffiliationSuggestion()
|
|
}
|
|
return $q.resolve(
|
|
`${userInputLocalAndDomain.local}@${universityDomain.hostname}`
|
|
)
|
|
})
|
|
.catch(function () {
|
|
_resetAffiliationSuggestion()
|
|
return $q.reject(null)
|
|
})
|
|
} else {
|
|
_resetAffiliationSuggestion()
|
|
return $q.reject(null)
|
|
}
|
|
}
|
|
|
|
$scope.linkInstitutionAcct = function (email, institutionId) {
|
|
_resetMakingRequestType()
|
|
$scope.ui.isMakingRequest = true
|
|
$scope.ui.isProcessing = true
|
|
$window.location.href = `${$scope.samlInitPath}?university_id=${institutionId}&auto=/user/settings&email=${email}`
|
|
}
|
|
|
|
$scope.selectUniversityManually = function () {
|
|
_resetAffiliationSuggestion()
|
|
$scope.ui.showManualUniversitySelectionUI = true
|
|
}
|
|
|
|
$scope.changeAffiliation = function (userEmail) {
|
|
if (_.get(userEmail, ['affiliation', 'institution', 'id'])) {
|
|
UserAffiliationsDataService.getUniversityDetails(
|
|
userEmail.affiliation.institution.id
|
|
).then(
|
|
universityDetails =>
|
|
($scope.affiliationToChange.university = universityDetails)
|
|
)
|
|
}
|
|
|
|
$scope.affiliationToChange.email = userEmail.email
|
|
$scope.affiliationToChange.role = userEmail.affiliation.role
|
|
$scope.affiliationToChange.department = userEmail.affiliation.department
|
|
}
|
|
|
|
$scope.saveAffiliationChange = function (userEmail) {
|
|
userEmail.affiliation.role = $scope.affiliationToChange.role
|
|
userEmail.affiliation.department = $scope.affiliationToChange.department
|
|
_resetAffiliationToChange()
|
|
return _monitorRequest(
|
|
UserAffiliationsDataService.addRoleAndDepartment(
|
|
userEmail.email,
|
|
userEmail.affiliation.role,
|
|
userEmail.affiliation.department
|
|
)
|
|
).then(() => setTimeout(() => _getUserEmails()))
|
|
}
|
|
|
|
$scope.cancelAffiliationChange = email => _resetAffiliationToChange()
|
|
|
|
$scope.isChangingAffiliation = email =>
|
|
$scope.affiliationToChange.email === email
|
|
|
|
$scope.showAddEmailForm = () => ($scope.ui.showAddEmailUI = true)
|
|
|
|
$scope.addNewEmail = function () {
|
|
let addEmailPromise
|
|
if (!$scope.newAffiliation.university) {
|
|
addEmailPromise = UserAffiliationsDataService.addUserEmail(
|
|
$scope.newAffiliation.email
|
|
)
|
|
} else {
|
|
if ($scope.newAffiliation.university.isUserSuggested) {
|
|
addEmailPromise =
|
|
UserAffiliationsDataService.addUserAffiliationWithUnknownUniversity(
|
|
$scope.newAffiliation.email,
|
|
$scope.newAffiliation.university.name,
|
|
$scope.newAffiliation.country.code,
|
|
$scope.newAffiliation.role,
|
|
$scope.newAffiliation.department
|
|
)
|
|
} else {
|
|
addEmailPromise = UserAffiliationsDataService.addUserAffiliation(
|
|
$scope.newAffiliation.email,
|
|
$scope.newAffiliation.university.id,
|
|
$scope.newAffiliation.role,
|
|
$scope.newAffiliation.department
|
|
)
|
|
}
|
|
}
|
|
|
|
$scope.ui.isAddingNewEmail = true
|
|
$scope.ui.showAddEmailUI = false
|
|
return _monitorRequest(addEmailPromise)
|
|
.then(function () {
|
|
_resetNewAffiliation()
|
|
_resetAddingEmail()
|
|
setTimeout(() => _getUserEmails())
|
|
})
|
|
.finally(() => ($scope.ui.isAddingNewEmail = false))
|
|
}
|
|
|
|
$scope.setDefaultUserEmail = userEmail =>
|
|
_monitorRequest(
|
|
UserAffiliationsDataService.setDefaultUserEmail(userEmail.email)
|
|
).then(function () {
|
|
for (const email of $scope.userEmails || []) {
|
|
email.default = false
|
|
}
|
|
userEmail.default = true
|
|
window.usersEmail = userEmail.email
|
|
$rootScope.usersEmail = userEmail.email
|
|
})
|
|
|
|
$scope.removeUserEmail = function (userEmail) {
|
|
$scope.userEmails = $scope.userEmails.filter(ue => ue !== userEmail)
|
|
return _monitorRequest(
|
|
UserAffiliationsDataService.removeUserEmail(userEmail.email)
|
|
)
|
|
.then(() => {
|
|
if (
|
|
userEmail.emailHasInstitutionLicence ||
|
|
(userEmail.affiliation && userEmail.affiliation.pastReconfirmDate)
|
|
) {
|
|
const stillHasLicenseAccess = $scope.userEmails.some(
|
|
ue => ue.emailHasInstitutionLicence
|
|
)
|
|
if (!stillHasLicenseAccess) {
|
|
try {
|
|
localStorage.setItem(
|
|
'showInstitutionalLeaversSurveyUntil',
|
|
Date.now() + ONE_WEEK_IN_MS
|
|
)
|
|
maybeShowInstitutionalLeaversSurvey()
|
|
} catch (e) {}
|
|
}
|
|
}
|
|
})
|
|
.catch(() => {
|
|
$scope.userEmails.push(userEmail)
|
|
})
|
|
}
|
|
|
|
$scope.resendConfirmationEmail = function (userEmail) {
|
|
_resetMakingRequestType()
|
|
$scope.ui.isResendingConfirmation = true
|
|
return _monitorRequest(
|
|
UserAffiliationsDataService.resendConfirmationEmail(userEmail.email)
|
|
).finally(() => ($scope.ui.isResendingConfirmation = false))
|
|
}
|
|
|
|
$scope.acknowledgeError = function () {
|
|
_reset()
|
|
return _getUserEmails()
|
|
}
|
|
|
|
const _resetAffiliationToChange = () =>
|
|
($scope.affiliationToChange = {
|
|
email: '',
|
|
university: null,
|
|
role: null,
|
|
department: null,
|
|
})
|
|
|
|
const _resetNewAffiliation = () =>
|
|
($scope.newAffiliation = {
|
|
email: '',
|
|
country: null,
|
|
university: null,
|
|
role: null,
|
|
department: null,
|
|
})
|
|
|
|
function _resetAddingEmail() {
|
|
$scope.ui.showAddEmailUI = false
|
|
$scope.ui.isValidEmail = false
|
|
$scope.ui.isBlacklistedEmail = false
|
|
$scope.ui.showManualUniversitySelectionUI = false
|
|
}
|
|
|
|
const _resetAffiliationSuggestion = () => {
|
|
$scope.newAffiliation = {
|
|
email: $scope.newAffiliation.email,
|
|
}
|
|
}
|
|
|
|
function _resetMakingRequestType() {
|
|
$scope.ui.isLoadingEmails = false
|
|
$scope.ui.isProcessing = false
|
|
$scope.ui.isResendingConfirmation = false
|
|
}
|
|
|
|
function _reset() {
|
|
$scope.ui = {
|
|
hasError: false,
|
|
errorMessage: '',
|
|
showChangeAffiliationUI: false,
|
|
isMakingRequest: false,
|
|
isLoadingEmails: false,
|
|
isAddingNewEmail: false,
|
|
}
|
|
_resetAffiliationToChange()
|
|
_resetNewAffiliation()
|
|
return _resetAddingEmail()
|
|
}
|
|
_reset()
|
|
$scope.ui.showAddEmailUI = window.location.hash === '#add-email'
|
|
|
|
function _monitorRequest(promise) {
|
|
$scope.ui.hasError = false
|
|
$scope.ui.isMakingRequest = true
|
|
promise
|
|
.catch(function (response) {
|
|
$scope.ui.hasError = true
|
|
$scope.ui.errorMessage = _.get(response, ['data', 'message'])
|
|
})
|
|
.finally(() => ($scope.ui.isMakingRequest = false))
|
|
return promise
|
|
}
|
|
|
|
$scope.inReconfirmNotificationPeriod = function (emailData) {
|
|
return _.get(emailData, ['affiliation', 'inReconfirmNotificationPeriod'])
|
|
}
|
|
|
|
$scope.institutionAlreadyLinked = function (emailData) {
|
|
const institutionId =
|
|
emailData.affiliation &&
|
|
emailData.affiliation.institution &&
|
|
emailData.affiliation.institution &&
|
|
emailData.affiliation.institution.id
|
|
? emailData.affiliation.institution.id.toString()
|
|
: undefined
|
|
return $scope.linkedInstitutionIds.indexOf(institutionId) !== -1
|
|
}
|
|
|
|
// Populates the emails table
|
|
function _getUserEmails() {
|
|
_resetMakingRequestType()
|
|
$scope.ui.isLoadingEmails = true
|
|
_monitorRequest(
|
|
UserAffiliationsDataService.getUserEmailsEnsureAffiliation()
|
|
)
|
|
.then(emails => {
|
|
$scope.userEmails = emails.map(email => {
|
|
email.ssoAvailable = _ssoAvailableForAffiliation(email.affiliation)
|
|
return email
|
|
})
|
|
$scope.linkedInstitutionIds = emails
|
|
.filter(email => {
|
|
return !!email.samlProviderId
|
|
})
|
|
.map(email => email.samlProviderId)
|
|
})
|
|
.finally(() => ($scope.ui.isLoadingEmails = false))
|
|
}
|
|
_getUserEmails()
|
|
}
|
|
)
|