Merge pull request #1995 from overleaf/ta-conversion-metrics-query-fix

Pass conversion metrics query params to v1

GitOrigin-RevId: 0f6cb0118ac92896590fbe26b24a6b2a723e1f2c
This commit is contained in:
Jessica Lawshe 2019-07-23 11:06:45 -05:00 committed by sharelatex
parent d9b5941642
commit 90b1c4a23c
7 changed files with 1770 additions and 0 deletions

View file

@ -0,0 +1,278 @@
/* eslint-disable
handle-callback-err,
max-len,
no-unused-vars,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
let LaunchpadController
const Settings = require('settings-sharelatex')
const Path = require('path')
const Url = require('url')
const logger = require('logger-sharelatex')
const metrics = require('metrics-sharelatex')
const UserRegistrationHandler = require('../../../../app/src/Features/User/UserRegistrationHandler')
const EmailHandler = require('../../../../app/src/Features/Email/EmailHandler')
const _ = require('underscore')
const UserGetter = require('../../../../app/src/Features/User/UserGetter')
const { User } = require('../../../../app/src/models/User')
const AuthenticationController = require('../../../../app/src/Features/Authentication/AuthenticationController')
module.exports = LaunchpadController = {
_getAuthMethod() {
if (Settings.ldap) {
return 'ldap'
} else if (Settings.saml) {
return 'saml'
} else {
return 'local'
}
},
launchpadPage(req, res, next) {
// TODO: check if we're using external auth?
// * how does all this work with ldap and saml?
const sessionUser = AuthenticationController.getSessionUser(req)
const authMethod = LaunchpadController._getAuthMethod()
return LaunchpadController._atLeastOneAdminExists(function(
err,
adminUserExists
) {
if (err != null) {
return next(err)
}
if (!sessionUser) {
if (!adminUserExists) {
return res.render(Path.resolve(__dirname, '../views/launchpad'), {
adminUserExists,
authMethod
})
} else {
return AuthenticationController._redirectToLoginPage(req, res)
}
} else {
return UserGetter.getUser(sessionUser._id, { isAdmin: 1 }, function(
err,
user
) {
if (err != null) {
return next(err)
}
if (user && user.isAdmin) {
return res.render(Path.resolve(__dirname, '../views/launchpad'), {
adminUserExists,
authMethod
})
} else {
return res.redirect('/restricted')
}
})
}
})
},
_atLeastOneAdminExists(callback) {
if (callback == null) {
callback = function(err, exists) {}
}
return UserGetter.getUser(
{ isAdmin: true },
{ _id: 1, isAdmin: 1 },
function(err, user) {
if (err != null) {
return callback(err)
}
return callback(null, user != null)
}
)
},
sendTestEmail(req, res, next) {
const { email } = req.body
if (!email) {
logger.log({}, 'no email address supplied')
return res.sendStatus(400)
}
logger.log({ email }, 'sending test email')
const emailOptions = { to: email }
return EmailHandler.sendEmail('testEmail', emailOptions, function(err) {
if (err != null) {
logger.warn({ email }, 'error sending test email')
return next(err)
}
logger.log({ email }, 'sent test email')
return res.sendStatus(201)
})
},
registerExternalAuthAdmin(authMethod) {
return function(req, res, next) {
if (LaunchpadController._getAuthMethod() !== authMethod) {
logger.log(
{ authMethod },
'trying to register external admin, but that auth service is not enabled, disallow'
)
return res.sendStatus(403)
}
const { email } = req.body
if (!email) {
logger.log({ authMethod }, 'no email supplied, disallow')
return res.sendStatus(400)
}
logger.log({ email }, 'attempted register first admin user')
return LaunchpadController._atLeastOneAdminExists(function(err, exists) {
if (err != null) {
return next(err)
}
if (exists) {
logger.log(
{ email },
'already have at least one admin user, disallow'
)
return res.sendStatus(403)
}
const body = {
email,
password: 'password_here',
first_name: email,
last_name: ''
}
logger.log(
{ body, authMethod },
'creating admin account for specified external-auth user'
)
return UserRegistrationHandler.registerNewUser(body, function(
err,
user
) {
if (err != null) {
logger.warn(
{ err, email, authMethod },
'error with registerNewUser'
)
return next(err)
}
return User.update(
{ _id: user._id },
{ $set: { isAdmin: true } },
function(err) {
if (err != null) {
logger.warn(
{ user_id: user._id, err },
'error setting user to admin'
)
return next(err)
}
AuthenticationController.setRedirectInSession(req, '/launchpad')
logger.log(
{ email, user_id: user._id, authMethod },
'created first admin account'
)
return res.json({ redir: '/launchpad', email })
}
)
})
})
}
},
registerAdmin(req, res, next) {
const { email } = req.body
const { password } = req.body
if (!email || !password) {
logger.log({}, 'must supply both email and password, disallow')
return res.sendStatus(400)
}
logger.log({ email }, 'attempted register first admin user')
return LaunchpadController._atLeastOneAdminExists(function(err, exists) {
if (err != null) {
return next(err)
}
if (exists) {
logger.log(
{ email: req.body.email },
'already have at least one admin user, disallow'
)
return res.sendStatus(403)
}
const body = { email, password }
return UserRegistrationHandler.registerNewUser(body, function(err, user) {
if (err != null) {
return next(err)
}
logger.log({ user_id: user._id }, 'making user an admin')
const proceed = () =>
User.update({ _id: user._id }, { $set: { isAdmin: true } }, function(
err
) {
if (err != null) {
logger.err(
{ user_id: user._id, err },
'error setting user to admin'
)
return next(err)
}
AuthenticationController.setRedirectInSession(req, '/launchpad')
logger.log(
{ email, user_id: user._id },
'created first admin account'
)
return res.json({
redir: '',
id: user._id.toString(),
first_name: user.first_name,
last_name: user.last_name,
email: user.email,
created: Date.now()
})
})
if (
Settings.overleaf != null &&
Settings.createV1AccountOnLogin != null
) {
logger.log(
{ user_id: user._id },
'Creating backing account in v1 for new admin user'
)
const SharelatexAuthController = require('../../../overleaf-integration/app/src/SharelatexAuth/SharelatexAuthController')
return UserGetter.getUser(user._id, function(err, user) {
if (err != null) {
return next(err)
}
return SharelatexAuthController._createBackingAccountIfNeeded(
user,
req,
function(err) {
if (err != null) {
return next(err)
}
return proceed()
}
)
})
} else {
return proceed()
}
})
})
}
}

View file

@ -0,0 +1,53 @@
/* eslint-disable
max-len,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const logger = require('logger-sharelatex')
const LaunchpadController = require('./LaunchpadController')
const AuthenticationController = require('../../../../app/src/Features/Authentication/AuthenticationController')
const AuthorizationMiddleware = require('../../../../app/src/Features/Authorization/AuthorizationMiddleware')
module.exports = {
apply(webRouter, apiRouter) {
logger.log({}, 'Init launchpad router')
webRouter.get('/launchpad', LaunchpadController.launchpadPage)
webRouter.post(
'/launchpad/register_admin',
LaunchpadController.registerAdmin
)
webRouter.post(
'/launchpad/register_ldap_admin',
LaunchpadController.registerExternalAuthAdmin('ldap')
)
webRouter.post(
'/launchpad/register_saml_admin',
LaunchpadController.registerExternalAuthAdmin('saml')
)
webRouter.post(
'/launchpad/send_test_email',
AuthorizationMiddleware.ensureUserIsSiteAdmin,
LaunchpadController.sendTestEmail
)
if (AuthenticationController.addEndpointToLoginWhitelist != null) {
AuthenticationController.addEndpointToLoginWhitelist('/launchpad')
AuthenticationController.addEndpointToLoginWhitelist(
'/launchpad/register_admin'
)
AuthenticationController.addEndpointToLoginWhitelist(
'/launchpad/register_ldap_admin'
)
return AuthenticationController.addEndpointToLoginWhitelist(
'/launchpad/register_saml_admin'
)
}
}
}

View file

@ -0,0 +1,267 @@
extends ../../../../app/views/layout
block content
script(type="text/javascript").
window.data = {
adminUserExists: !{adminUserExists == true},
ideJsPath: "!{buildJsPath('ide.js')}",
authMethod: "!{authMethod}"
}
script(type="text/javascript" src='/socket.io/socket.io.js')
style.
hr { margin-bottom: 5px; }
.status-check {
margin-bottom: 15px;
}
.alert { margin-top: 15px; margin-bottom: 15px; }
a small { cursor: pointer; color: #a93529; }
.launchpad-body img { width: 150px; margin-top: 15px; }
.content.content-alt(ng-controller="LaunchpadController")
.container(ng-cloak)
.row
.col-md-8.col-md-offset-2
.card.launchpad-body
.row
.col-md-12
.text-center
h1 #{translate('welcome_to_sl')}
p
img(src=buildImgPath('/brand/lion.svg'))
<!-- wrapper -->
.row
.col-md-8.col-md-offset-2
<!-- create first admin form -->
.row(ng-if="shouldShowAdminForm()")
.col-md-12
h2 #{translate('create_first_admin_account')}
// Local Auth Form
div(ng-if="authMethod == 'local'")
form(async-form="register", name="registerForm", on-success="onCreateAdminSuccess"
on-error="onCreateAdminError"
action="/launchpad/register_admin", method="POST", ng-cloak)
input(name='_csrf', type='hidden', value=csrfToken)
form-messages(for="registerForm")
.form-group
label(for='email') #{translate("email")}
input.form-control(
type='email',
name='email',
placeholder="email@example.com"
required,
ng-model="email",
ng-model-options="{ updateOn: 'blur' }",
focus="true"
)
span.small.text-primary(ng-show="registerForm.email.$invalid && registerForm.email.$dirty")
| #{translate("must_be_email_address")}
.form-group
label(for='password') #{translate("password")}
input.form-control#passwordField(
type='password',
name='password',
placeholder="********",
required,
ng-model="password",
complex-password
)
span.small.text-primary(ng-show="registerForm.password.$error.complexPassword",
ng-bind-html="complexPasswordErrorMessage")
.actions
button.btn-primary.btn(
type='submit'
ng-disabled="registerForm.inflight || registerForm.password.$error.required|| registerForm.password.$error.complexPassword || createAdminSuccess"
)
span(ng-show="!registerForm.inflight") #{translate("register")}
span(ng-show="registerForm.inflight") #{translate("registering")}...
// Ldap Form
div(ng-if="authMethod == 'ldap'")
h3 #{translate('ldap')}
p
| #{translate('ldap_create_admin_instructions')}
form(async-form="register", name="registerLdapForm", on-success="onCreateAdminSuccess"
on-error="onCreateAdminError"
action="/launchpad/register_ldap_admin", method="POST", ng-cloak)
input(name='_csrf', type='hidden', value=csrfToken)
form-messages(for="registerLdapForm")
.form-group
label(for='email') #{translate("email")}
input.form-control(
type='email',
name='email',
placeholder="email@example.com"
required,
ng-model="email",
ng-model-options="{ updateOn: 'blur' }",
focus="true"
)
span.small.text-primary(ng-show="registerLdapForm.email.$invalid && registerLdapForm.email.$dirty")
| #{translate("must_be_email_address")}
.actions
button.btn-primary.btn(
type='submit'
ng-disabled="registerLdapForm.inflight || registerLdapForm.password.$error.required|| registerLdapForm.password.$error.complexPassword || createAdminSuccess"
)
span(ng-show="!registerLdapForm.inflight") #{translate("register")}
span(ng-show="registerLdapForm.inflight") #{translate("registering")}...
// Saml Form
div(ng-if="authMethod == 'saml'")
h3 #{translate('saml')}
p
| #{translate('saml_create_admin_instructions')}
form(async-form="register", name="registerSamlForm", on-success="onCreateAdminSuccess"
on-error="onCreateAdminError"
action="/launchpad/register_saml_admin", method="POST", ng-cloak)
input(name='_csrf', type='hidden', value=csrfToken)
form-messages(for="registerSamlForm")
.form-group
label(for='email') #{translate("email")}
input.form-control(
type='email',
name='email',
placeholder="email@example.com"
required,
ng-model="email",
ng-model-options="{ updateOn: 'blur' }",
focus="true"
)
span.small.text-primary(ng-show="registerSamlForm.email.$invalid && registerSamlForm.email.$dirty")
| #{translate("must_be_email_address")}
.actions
button.btn-primary.btn(
type='submit'
ng-disabled="registerSamlForm.inflight || registerSamlForm.password.$error.required|| registerSamlForm.password.$error.complexPassword || createAdminSuccess"
)
span(ng-show="!registerSamlForm.inflight") #{translate("register")}
span(ng-show="registerSamlForm.inflight") #{translate("registering")}...
<!-- success message -->
.row(ng-if="createAdminSuccess")
.col-md-12.text-center
.alert.alert-success
| !{translate('admin_user_created_message', {link: '/login?redir=/launchpad'})}
<!-- error message -->
.row(ng-if="createAdminError")
.col-md-12.text-center
.alert.alert-danger
| #{translate('generic_something_went_wrong')}
br
<!-- status indicators -->
div(ng-if="!shouldShowAdminForm()")
.row
.col-md-12.status-indicators
h2 #{translate('status_checks')}
<!-- ide js -->
.row.status-check
.col-sm-5
| #{translate('editor_resources')}
.col-sm-7
div(ng-switch="statusChecks.ideJs.status")
span(ng-switch-when="inflight")
i.fa.fa-fw.fa-spinner.fa-spin
span &nbsp;#{translate('checking')}
span(ng-switch-when="ok")
i.fa.fa-check
span &nbsp;#{translate('ok')}
a(ng-click="tryFetchIdeJs()")
small &nbsp;#{translate('retry')}
span(ng-switch-when="error")
i.fa.fa-exclamation
span &nbsp;#{translate('error')}
a(ng-click="tryFetchIdeJs()")
small &nbsp;#{translate('retry')}
div.alert.alert-danger
| {{ statusChecks.ideJs.error }}
<!-- websocket -->
.row.status-check
.col-sm-5
| #{translate('websockets')}
.col-sm-7
div(ng-switch="statusChecks.websocket.status")
span(ng-switch-when="inflight")
i.fa.fa-fw.fa-spinner.fa-spin
span &nbsp;#{translate('checking')}
span(ng-switch-when="ok")
i.fa.fa-check
span &nbsp;#{translate('ok')}
a(ng-click="tryOpenWebSocket()")
small &nbsp;#{translate('retry')}
span(ng-switch-when="error")
i.fa.fa-exclamation
span &nbsp;#{translate('error')}
a(ng-click="tryOpenWebSocket()")
small &nbsp;#{translate('retry')}
div.alert.alert-danger
| {{ statusChecks.websocket.error }}
<!-- break -->
hr
<!-- other actions -->
.row
.col-md-12
h2 #{translate('other_actions')}
h3 #{translate('send_test_email')}
form.form
.form-group
label(for="emailInput") Email
input(type="text", name="emailInput" ng-model="testEmail.emailAddress").form-control
button(ng-click="sendTestEmail()", ng-disabled="testEmail.inflight").btn.btn-primary
span(ng-show="!testEmail.inflight") #{translate("send")}
span(ng-show="testEmail.inflight") #{translate("sending")}...
div(ng-if="testEmail.status == 'ok'")
.alert.alert-success
| #{translate('email_sent')}
div(ng-if="testEmail.status == 'error'")
.alert.alert-danger
| #{translate('generic_something_went_wrong')}
<!-- break -->
hr
<!-- Go to ShareLaTex -->
.row
.col-md-12
.text-center
br
p
a(href="/admin").btn.btn-md
| Go To Admin Panel
| &nbsp;
a(href="/project").btn.btn-md.btn-primary
| Start Using ShareLaTeX
br

View file

@ -0,0 +1,9 @@
/* eslint-disable
no-unused-vars,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
let Launchpad
const LaunchpadRouter = require('./app/src/LaunchpadRouter')
module.exports = Launchpad = { router: LaunchpadRouter }

View file

@ -0,0 +1,155 @@
/* eslint-disable
max-len,
no-return-assign,
no-undef,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
define(['base'], App =>
App.controller('LaunchpadController', function($scope, $http, $timeout) {
$scope.adminUserExists = window.data.adminUserExists
$scope.ideJsPath = window.data.ideJsPath
$scope.authMethod = window.data.authMethod
$scope.createAdminSuccess = null
$scope.createAdminError = null
$scope.statusChecks = {
ideJs: { status: 'inflight', error: null },
websocket: { status: 'inflight', error: null },
healthCheck: { status: 'inflight', error: null }
}
$scope.testEmail = {
emailAddress: '',
inflight: false,
status: null // | 'ok' | 'success'
}
$scope.shouldShowAdminForm = () => !$scope.adminUserExists
$scope.onCreateAdminSuccess = function(response) {
const { status } = response
if (status >= 200 && status < 300) {
return ($scope.createAdminSuccess = true)
}
}
$scope.onCreateAdminError = () => ($scope.createAdminError = true)
$scope.sendTestEmail = function() {
$scope.testEmail.inflight = true
$scope.testEmail.status = null
return $http
.post('/launchpad/send_test_email', {
email: $scope.testEmail.emailAddress,
_csrf: window.csrfToken
})
.then(function(response) {
const { status } = response
$scope.testEmail.inflight = false
if (status >= 200 && status < 300) {
return ($scope.testEmail.status = 'ok')
}
})
.catch(function() {
$scope.testEmail.inflight = false
return ($scope.testEmail.status = 'error')
})
}
$scope.tryFetchIdeJs = function() {
$scope.statusChecks.ideJs.status = 'inflight'
return $timeout(
() =>
$http
.get($scope.ideJsPath)
.then(function(response) {
const { status } = response
if (status >= 200 && status < 300) {
return ($scope.statusChecks.ideJs.status = 'ok')
}
})
.catch(function(response) {
const { status } = response
$scope.statusChecks.ideJs.status = 'error'
return ($scope.statusChecks.ideJs.error = new Error(
`Http status: ${status}`
))
}),
1000
)
}
$scope.tryOpenWebSocket = function() {
$scope.statusChecks.websocket.status = 'inflight'
return $timeout(function() {
if (typeof io === 'undefined' || io === null) {
$scope.statusChecks.websocket.status = 'error'
$scope.statusChecks.websocket.error = 'socket.io not loaded'
return
}
const socket = io.connect(
null,
{
reconnect: false,
'connect timeout': 30 * 1000,
'force new connection': true
}
)
socket.on('connectionAccepted', function() {
$scope.statusChecks.websocket.status = 'ok'
return $scope.$apply(function() {})
})
socket.on('connectionRejected', function(err) {
$scope.statusChecks.websocket.status = 'error'
$scope.statusChecks.websocket.error = err
return $scope.$apply(function() {})
})
return socket.on('connect_failed', function(err) {
$scope.statusChecks.websocket.status = 'error'
$scope.statusChecks.websocket.error = err
return $scope.$apply(function() {})
})
}, 1000)
}
$scope.tryHealthCheck = function() {
$scope.statusChecks.healthCheck.status = 'inflight'
return $http
.get('/health_check')
.then(function(response) {
const { status } = response
if (status >= 200 && status < 300) {
return ($scope.statusChecks.healthCheck.status = 'ok')
}
})
.catch(function(response) {
const { status } = response
$scope.statusChecks.healthCheck.status = 'error'
return ($scope.statusChecks.healthCheck.error = new Error(
`Http status: ${status}`
))
})
}
$scope.runStatusChecks = function() {
$timeout(() => $scope.tryFetchIdeJs(), 1000)
return $timeout(() => $scope.tryOpenWebSocket(), 2000)
}
// kick off the status checks on load
if ($scope.adminUserExists) {
return $scope.runStatusChecks()
}
}))

View file

@ -0,0 +1,3 @@
// TODO: This file was created by bulk-decaffeinate.
// Sanity-check the conversion and remove this comment.
define(['main/launchpad/controllers/LaunchpadController'], function() {})

File diff suppressed because it is too large Load diff