mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge branch 'private_registration'
This commit is contained in:
commit
ff55e4c5ed
21 changed files with 270 additions and 224 deletions
|
@ -290,3 +290,37 @@ module.exports = (grunt) ->
|
|||
grunt.registerTask 'default', 'run'
|
||||
|
||||
grunt.registerTask 'version', "Write the version number into sentry.jade", ['git-rev-parse', 'sed']
|
||||
|
||||
grunt.registerTask 'create-admin-user', "Create a user with the given email address and make them an admin. Update in place if the user already exists", () ->
|
||||
done = @async()
|
||||
email = grunt.option("email")
|
||||
if !email?
|
||||
console.error "Usage: grunt create-admin-user --email joe@example.com"
|
||||
process.exit(1)
|
||||
|
||||
settings = require "settings-sharelatex"
|
||||
UserRegistrationHandler = require "./app/js/Features/User/UserRegistrationHandler"
|
||||
PasswordResetTokenHandler = require "./app/js/Features/PasswordReset/PasswordResetTokenHandler"
|
||||
UserRegistrationHandler.registerNewUser {
|
||||
email: email
|
||||
password: require("crypto").randomBytes(32).toString("hex")
|
||||
}, (error, user) ->
|
||||
if error? and error?.message != "EmailAlreadyRegistered"
|
||||
throw error
|
||||
user.isAdmin = true
|
||||
user.save (error) ->
|
||||
throw error if error?
|
||||
ONE_WEEK = 7 * 24 * 60 * 60 # seconds
|
||||
PasswordResetTokenHandler.getNewToken user._id, { expiresIn: ONE_WEEK }, (err, token)->
|
||||
return next(err) if err?
|
||||
|
||||
console.log ""
|
||||
console.log """
|
||||
Successfully created #{email} as an admin user.
|
||||
|
||||
Please visit the following URL to set a password for #{email} and log in:
|
||||
|
||||
#{settings.siteUrl}/user/password/set?passwordResetToken=#{token}
|
||||
|
||||
"""
|
||||
done()
|
|
@ -18,7 +18,7 @@ Server.app.use (error, req, res, next) ->
|
|||
logger.error err: error, url:req.url, method:req.method, user:req?.sesson?.user, "error passed to top level next middlewear"
|
||||
res.statusCode = error.status or 500
|
||||
if res.statusCode == 500
|
||||
res.end("Oops, something went wrong with your request, sorry. If this continues, please contact us at support@sharelatex.com")
|
||||
res.end("Oops, something went wrong with your request, sorry. If this continues, please contact us at #{Settings.adminEmail}")
|
||||
else
|
||||
res.end()
|
||||
|
||||
|
|
|
@ -6,25 +6,19 @@ settings = require("settings-sharelatex")
|
|||
|
||||
templates = {}
|
||||
|
||||
templates.welcome =
|
||||
subject: _.template "Welcome to ShareLaTeX"
|
||||
templates.registered =
|
||||
subject: _.template "Activate your #{settings.appName} Account"
|
||||
layout: PersonalEmailLayout
|
||||
type:"lifecycle"
|
||||
compiledTemplate: _.template '''
|
||||
<p>Hi <%= first_name %>,</p>
|
||||
type: "notification"
|
||||
compiledTemplate: _.template """
|
||||
<p>Congratulations, you've just had an account created for you on #{settings.appName} with the email address "<%= to %>".</p>
|
||||
|
||||
<p>Thanks for signing up to ShareLaTeX! If you ever get lost, you can log in again <a href="<%= siteUrl %>/login">here</a> with the email address "<%= to %>".</p>
|
||||
<p><a href="<%= setNewPasswordUrl %>">Click here to set your password and log in.</a></p>
|
||||
|
||||
<p>If you're new to LaTeX, take a look at our <a href="<%= siteUrl %>/learn">Help Guides</a> and <a href="<%= siteUrl %>/templates">Templates</a>.</p>
|
||||
<p>Once you have reset your password you can <a href="#{settings.siteUrl}/login">log in here</a>.</p>
|
||||
|
||||
<p>
|
||||
Regards, <br>
|
||||
Henry <br>
|
||||
ShareLaTeX Co-founder
|
||||
</p>
|
||||
|
||||
<p>PS. We love talking to our users about ShareLaTeX. Reply to this email to get in touch us with us directly, whatever the reason. Questions, comments, problems, suggestions, all welcome!<p>
|
||||
'''
|
||||
<p>If you have any questions or problems, please contact <a href="mailto:#{settings.adminEmail}">#{settings.adminEmail}</a>.</p>
|
||||
"""
|
||||
|
||||
templates.canceledSubscription =
|
||||
subject: _.template "ShareLaTeX thoughts"
|
||||
|
@ -44,16 +38,16 @@ ShareLaTeX Co-founder
|
|||
'''
|
||||
|
||||
templates.passwordResetRequested =
|
||||
subject: _.template "Password Reset - ShareLatex.com"
|
||||
subject: _.template "Password Reset - #{settings.appName}"
|
||||
layout: NotificationEmailLayout
|
||||
type:"notification"
|
||||
compiledTemplate: _.template '''
|
||||
<h1 class="h1">Password Reset</h1>
|
||||
compiledTemplate: _.template """
|
||||
<h2>Password Reset</h2>
|
||||
<p>
|
||||
We got a request to reset your ShareLaTeX password.
|
||||
We got a request to reset your #{settings.appName} password.
|
||||
<p>
|
||||
<center>
|
||||
<div style="width:200px;background-color:#a93629;border:1px solid #e24b3b;border-radius:3px;padding:15px; margin:12.5px;">
|
||||
<div style="width:200px;background-color:#a93629;border:1px solid #e24b3b;border-radius:3px;padding:15px; margin:24px;">
|
||||
<div style="padding-right:10px;padding-left:10px">
|
||||
<a href="<%= setNewPasswordUrl %>" style="text-decoration:none" target="_blank">
|
||||
<span style= "font-size:16px;font-family:Arial;font-weight:bold;color:#fff;white-space:nowrap;display:block; text-align:center">
|
||||
|
@ -70,18 +64,17 @@ If you didn't request a password reset, let us know.
|
|||
|
||||
</p>
|
||||
<p>Thank you</p>
|
||||
<p> <a href="<%= siteUrl %>"> ShareLatex.com </a></p>
|
||||
'''
|
||||
<p> <a href="<%= siteUrl %>">#{settings.appName}</a></p>
|
||||
"""
|
||||
|
||||
templates.projectSharedWithYou =
|
||||
subject: _.template "<%= owner.email %> wants to share <%= project.name %> with you"
|
||||
layout: NotificationEmailLayout
|
||||
type:"notification"
|
||||
compiledTemplate: _.template '''
|
||||
compiledTemplate: _.template """
|
||||
<p>Hi, <%= owner.email %> wants to share <a href="<%= project.url %>">'<%= project.name %>'</a> with you</p>
|
||||
<p> </p>
|
||||
<center>
|
||||
<div style="width:200px;background-color:#a93629;border:1px solid #e24b3b;border-radius:3px;padding:15px; margin:12.5px;">
|
||||
<div style="width:200px;background-color:#a93629;border:1px solid #e24b3b;border-radius:3px;padding:15px; margin:24px;">
|
||||
<div style="padding-right:10px;padding-left:10px">
|
||||
<a href="<%= project.url %>" style="text-decoration:none" target="_blank">
|
||||
<span style= "font-size:16px;font-family:Helvetica,Arial;font-weight:400;color:#fff;white-space:nowrap;display:block; text-align:center">
|
||||
|
@ -92,11 +85,11 @@ templates.projectSharedWithYou =
|
|||
</div>
|
||||
</center>
|
||||
<p> Thank you</p>
|
||||
<p> <a href="<%= siteUrl %>"> ShareLatex.com </a></p>
|
||||
|
||||
'''
|
||||
<p> <a href="<%= siteUrl %>">#{settings.appName}</a></p>
|
||||
"""
|
||||
|
||||
module.exports =
|
||||
templates: templates
|
||||
|
||||
buildEmail: (templateName, opts)->
|
||||
template = templates[templateName]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
_ = require("underscore")
|
||||
settings = require "settings-sharelatex"
|
||||
|
||||
module.exports = _.template '''
|
||||
module.exports = _.template """
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
|
@ -311,12 +312,8 @@ module.exports = _.template '''
|
|||
<!-- // Begin Template Header \\ -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="600" id="templateHeader">
|
||||
<tr>
|
||||
<td class="headerContent" style="padding: 25px;border-bottom:#dadf90;background-color:#F6F6F6;text-align:left;">
|
||||
|
||||
<!-- // Begin Module: Standard Header Image \\ -->
|
||||
<img src="https://www.sharelatex.com/img/logo.png" style="max-width:600px;" id="headerImage campaign-icon" />
|
||||
<!-- // End Module: Standard Header Image \\ -->
|
||||
|
||||
<td class="headerContent" style="padding: 25px;border-bottom:#dadf90;background-color:#F6F6F6;text-align:left;font-size:18px">
|
||||
#{settings.appName}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -346,31 +343,6 @@ module.exports = _.template '''
|
|||
<!-- // End Template Body \\ -->
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top">
|
||||
<!-- // Begin Template Footer \\ -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="600" id="templateFooter">
|
||||
<tr>
|
||||
<td valign="top" class="footerContent">
|
||||
|
||||
<!-- // Begin Module: Standard Footer \\ -->
|
||||
<table border="0" cellpadding="25" cellspacing="0" width="100%">
|
||||
<tr>
|
||||
<td colspan="2" valign="middle" id="social">
|
||||
<div>
|
||||
<a href="http://twitter.com/#!/sharelatex">Follow on Twitter</a> | <a href="http://www.facebook.com/pages/ShareLaTeX/301671376556660">Friend on Facebook</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- // End Module: Standard Footer \\ -->
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- // End Template Footer \\ -->
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
|
@ -380,4 +352,4 @@ module.exports = _.template '''
|
|||
</body>
|
||||
</html>
|
||||
|
||||
'''
|
||||
"""
|
|
@ -10,12 +10,17 @@ buildKey = (token)-> return "password_token:#{token}"
|
|||
|
||||
module.exports =
|
||||
|
||||
getNewToken: (user_id, callback)->
|
||||
getNewToken: (user_id, options = {}, callback)->
|
||||
# options is optional
|
||||
if typeof options == "function"
|
||||
callback = options
|
||||
options = {}
|
||||
expiresIn = options.expiresIn or ONE_HOUR_IN_S
|
||||
logger.log user_id:user_id, "generating token for password reset"
|
||||
token = crypto.randomBytes(32).toString("hex")
|
||||
multi = rclient.multi()
|
||||
multi.set buildKey(token), user_id
|
||||
multi.expire buildKey(token), ONE_HOUR_IN_S
|
||||
multi.expire buildKey(token), expiresIn
|
||||
multi.exec (err)->
|
||||
callback(err, token)
|
||||
|
||||
|
|
|
@ -39,11 +39,14 @@ module.exports = AdminController =
|
|||
|
||||
SystemMessageManager.getMessagesFromDB (error, systemMessages) ->
|
||||
return next(error) if error?
|
||||
res.render 'admin',
|
||||
res.render 'admin/index',
|
||||
title: 'System Admin'
|
||||
openSockets: openSockets
|
||||
systemMessages: systemMessages
|
||||
|
||||
registerNewUser: (req, res, next) ->
|
||||
res.render 'admin/register'
|
||||
|
||||
dissconectAllUsers: (req, res)=>
|
||||
logger.warn "disconecting everyone"
|
||||
EditorRealTimeController.emitToAll 'forceDisconnect', "Sorry, we are performing a quick update to the editor and need to close it down. Please refresh the page to continue."
|
||||
|
|
|
@ -6,11 +6,13 @@ UserRegistrationHandler = require("./UserRegistrationHandler")
|
|||
logger = require("logger-sharelatex")
|
||||
metrics = require("../../infrastructure/Metrics")
|
||||
Url = require("url")
|
||||
AuthenticationController = require("../Authentication/AuthenticationController")
|
||||
AuthenticationManager = require("../Authentication/AuthenticationManager")
|
||||
ReferalAllocator = require("../Referal/ReferalAllocator")
|
||||
UserUpdater = require("./UserUpdater")
|
||||
SubscriptionDomainAllocator = require("../Subscription/SubscriptionDomainAllocator")
|
||||
EmailHandler = require("../Email/EmailHandler")
|
||||
PasswordResetTokenHandler = require "../PasswordReset/PasswordResetTokenHandler"
|
||||
settings = require "settings-sharelatex"
|
||||
crypto = require "crypto"
|
||||
|
||||
module.exports =
|
||||
|
||||
|
@ -80,28 +82,36 @@ module.exports =
|
|||
res.redirect '/login'
|
||||
|
||||
register : (req, res, next = (error) ->)->
|
||||
logger.log email: req.body.email, "attempted register"
|
||||
redir = Url.parse(req.body.redir or "/project").path
|
||||
UserRegistrationHandler.registerNewUser req.body, (err, user)->
|
||||
if err == "EmailAlreadyRegisterd"
|
||||
return AuthenticationController.login req, res
|
||||
else if err?
|
||||
next(err)
|
||||
else
|
||||
metrics.inc "user.register.success"
|
||||
ReferalAllocator.allocate req.session.referal_id, user._id, req.session.referal_source, req.session.referal_medium
|
||||
SubscriptionDomainAllocator.autoAllocate(user)
|
||||
AuthenticationController.establishUserSession req, user, (error) ->
|
||||
return callback(error) if error?
|
||||
req.session.justRegistered = true
|
||||
res.send
|
||||
redir:redir
|
||||
id:user._id.toString()
|
||||
first_name: user.first_name
|
||||
last_name: user.last_name
|
||||
email: user.email
|
||||
created: Date.now()
|
||||
email = req.body.email
|
||||
if !email? or email == ""
|
||||
res.send 422 # Unprocessable Entity
|
||||
return
|
||||
logger.log {email}, "registering new user"
|
||||
UserRegistrationHandler.registerNewUser {
|
||||
email: email
|
||||
password: crypto.randomBytes(32).toString("hex")
|
||||
}, (err, user)->
|
||||
if err? and err?.message != "EmailAlreadyRegistered"
|
||||
return next(err)
|
||||
|
||||
if err?.message == "EmailAlreadyRegistered"
|
||||
logger.log {email}, "user already exists, resending welcome email"
|
||||
|
||||
ONE_WEEK = 7 * 24 * 60 * 60 # seconds
|
||||
PasswordResetTokenHandler.getNewToken user._id, { expiresIn: ONE_WEEK }, (err, token)->
|
||||
return next(err) if err?
|
||||
|
||||
setNewPasswordUrl = "#{settings.siteUrl}/user/password/set?passwordResetToken=#{token}"
|
||||
|
||||
EmailHandler.sendEmail "registered", {
|
||||
to: user.email
|
||||
setNewPasswordUrl: setNewPasswordUrl
|
||||
}, () ->
|
||||
|
||||
res.json {
|
||||
email: user.email
|
||||
setNewPasswordUrl: setNewPasswordUrl
|
||||
}
|
||||
|
||||
changePassword : (req, res, next = (error) ->)->
|
||||
metrics.inc "user.password-change"
|
||||
|
|
|
@ -4,7 +4,6 @@ UserCreator = require("./UserCreator")
|
|||
AuthenticationManager = require("../Authentication/AuthenticationManager")
|
||||
NewsLetterManager = require("../Newsletter/NewsletterManager")
|
||||
async = require("async")
|
||||
EmailHandler = require("../Email/EmailHandler")
|
||||
logger = require("logger-sharelatex")
|
||||
|
||||
module.exports =
|
||||
|
@ -40,13 +39,13 @@ module.exports =
|
|||
self = @
|
||||
requestIsValid = @_registrationRequestIsValid userDetails
|
||||
if !requestIsValid
|
||||
return callback("request is not valid")
|
||||
return callback(new Error("request is not valid"))
|
||||
userDetails.email = userDetails.email?.trim()?.toLowerCase()
|
||||
User.findOne email:userDetails.email, (err, user)->
|
||||
if err?
|
||||
return callback err
|
||||
if user?.holdingAccount == false
|
||||
return callback("EmailAlreadyRegisterd")
|
||||
return callback(new Error("EmailAlreadyRegistered"), user)
|
||||
self._createNewUserIfRequired user, userDetails, (err, user)->
|
||||
if err?
|
||||
return callback(err)
|
||||
|
@ -56,11 +55,6 @@ module.exports =
|
|||
(cb)->
|
||||
NewsLetterManager.subscribe user, ->
|
||||
cb() #this can be slow, just fire it off
|
||||
(cb)->
|
||||
emailOpts =
|
||||
first_name:user.first_name
|
||||
to: user.email
|
||||
EmailHandler.sendEmail "welcome", emailOpts, cb
|
||||
], (err)->
|
||||
logger.log user: user, "registered"
|
||||
callback(err, user)
|
||||
|
|
|
@ -54,8 +54,8 @@ module.exports = class Router
|
|||
app.get '/logout', UserController.logout
|
||||
app.get '/restricted', SecurityManager.restricted
|
||||
|
||||
# Left as a placeholder for implementing a public register page
|
||||
app.get '/register', UserPagesController.registerPage
|
||||
app.post '/register', UserController.register
|
||||
|
||||
EditorRouter.apply(app, httpAuth)
|
||||
CollaboratorsRouter.apply(app)
|
||||
|
@ -157,6 +157,8 @@ module.exports = class Router
|
|||
|
||||
#Admin Stuff
|
||||
app.get '/admin', SecurityManager.requestIsAdmin, AdminController.index
|
||||
app.get '/admin/register', SecurityManager.requestIsAdmin, AdminController.registerNewUser
|
||||
app.post '/admin/register', SecurityManager.requestIsAdmin, UserController.register
|
||||
app.post '/admin/closeEditor', SecurityManager.requestIsAdmin, AdminController.closeEditor
|
||||
app.post '/admin/dissconectAllUsers', SecurityManager.requestIsAdmin, AdminController.dissconectAllUsers
|
||||
app.post '/admin/syncUserToSubscription', SecurityManager.requestIsAdmin, AdminController.syncUserToSubscription
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
extends layout
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
.content.content-alt
|
40
services/web/app/views/admin/register.jade
Normal file
40
services/web/app/views/admin/register.jade
Normal file
|
@ -0,0 +1,40 @@
|
|||
extends ../layout
|
||||
|
||||
block content
|
||||
.content.content-alt
|
||||
.container
|
||||
.row
|
||||
.col-md-12
|
||||
.card(ng-controller="RegisterUsersController")
|
||||
.page-header
|
||||
h1 Register New Users
|
||||
form.form
|
||||
.row
|
||||
.col-md-4.col-xs-8
|
||||
input.form-control(
|
||||
name="email",
|
||||
type="text",
|
||||
placeholder="jane@example.com, joe@example.com",
|
||||
ng-model="inputs.emails",
|
||||
on-enter="registerUsers()"
|
||||
)
|
||||
.col-md-8.col-xs-4
|
||||
button.btn.btn-primary(ng-click="registerUsers()") #{translate("register")}
|
||||
|
||||
.row-spaced(ng-show="error").ng-cloak.text-danger
|
||||
p Sorry, an error occured
|
||||
|
||||
.row-spaced(ng-show="users.length > 0").ng-cloak.text-success
|
||||
p We've sent out welcome emails to the registered users.
|
||||
p You can also manually send them URLs below to allow them to reset their password and log in for the first time.
|
||||
p (Password reset tokens will expire after one week and the user will need registering again).
|
||||
|
||||
hr(ng-show="users.length > 0").ng-cloak
|
||||
table(ng-show="users.length > 0").table.table-striped.ng-cloak
|
||||
tr
|
||||
th #{translate("email")}
|
||||
th Set Password Url
|
||||
tr(ng-repeat="user in users")
|
||||
td {{ user.email }}
|
||||
td(style="word-break: break-all;") {{ user.setNewPasswordUrl }}
|
||||
|
|
@ -11,6 +11,15 @@ nav.navbar.navbar-default
|
|||
.navbar-collapse.collapse(collapse="navCollapsed")
|
||||
|
||||
ul.nav.navbar-nav.navbar-right
|
||||
if (session && session.user && session.user.isAdmin)
|
||||
li.dropdown(class="subdued")
|
||||
a.dropdown-toggle(href)
|
||||
| Admin
|
||||
b.caret
|
||||
ul.dropdown-menu
|
||||
li
|
||||
a(href="/admin/register") Register New Users
|
||||
|
||||
each item in nav.header
|
||||
if ((item.only_when_logged_in && session && session.user) || (item.only_when_logged_out && (!session || !session.user)) || (!item.only_when_logged_out && !item.only_when_logged_in))
|
||||
if item.dropdown
|
||||
|
|
|
@ -16,43 +16,11 @@ block content
|
|||
a(href="/login") #{translate("login_here")}
|
||||
|
||||
.row
|
||||
.col-md-6.col-md-offset-3.col-lg-4.col-lg-offset-4
|
||||
.col-md-8.col-md-offset-2.col-lg-6.col-lg-offset-3
|
||||
.card
|
||||
.page-header
|
||||
h1 #{translate("register")}
|
||||
form(async-form="register", name="registerForm", action="/register", method="POST", ng-cloak)
|
||||
input(name='_csrf', type='hidden', value=csrfToken)
|
||||
input(name='redir', type='hidden', value=redir)
|
||||
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-init="email = #{JSON.stringify(new_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(
|
||||
type='password',
|
||||
name='password',
|
||||
placeholder="********",
|
||||
required,
|
||||
ng-model="password"
|
||||
)
|
||||
span.small.text-primary(ng-show="registerForm.password.$invalid && registerForm.password.$dirty")
|
||||
| #{translate("required")}
|
||||
.actions
|
||||
button.btn-primary.btn(
|
||||
type='submit'
|
||||
ng-disabled="registerForm.inflight"
|
||||
)
|
||||
span(ng-show="!registerForm.inflight") #{translate("register")}
|
||||
span(ng-show="registerForm.inflight") #{translate("registering")}...
|
||||
p
|
||||
| Please contact
|
||||
strong #{settings.adminEmail}
|
||||
| to create an account.
|
||||
|
|
|
@ -266,6 +266,7 @@ module.exports =
|
|||
# projectId: ""
|
||||
|
||||
appName: "ShareLaTeX (Community Edition)"
|
||||
adminEmail: "placeholder@example.com"
|
||||
|
||||
nav:
|
||||
title: "ShareLaTeX Community Edition"
|
||||
|
|
|
@ -14,6 +14,7 @@ define [
|
|||
"main/subscription-dashboard"
|
||||
"main/new-subscription"
|
||||
"main/annual-upgrade"
|
||||
"main/register-users"
|
||||
"analytics/AbTestingManager"
|
||||
"directives/asyncForm"
|
||||
"directives/stopPropagation"
|
||||
|
|
32
services/web/public/coffee/main/register-users.coffee
Normal file
32
services/web/public/coffee/main/register-users.coffee
Normal file
|
@ -0,0 +1,32 @@
|
|||
define [
|
||||
"base"
|
||||
], (App) ->
|
||||
App.controller "RegisterUsersController", ($scope, queuedHttp) ->
|
||||
$scope.users = []
|
||||
|
||||
$scope.inputs =
|
||||
emails: ""
|
||||
|
||||
parseEmails = (emailsString)->
|
||||
regexBySpaceOrComma = /[\s,]+/
|
||||
emails = emailsString.split(regexBySpaceOrComma)
|
||||
emails = _.map emails, (email)->
|
||||
email = email.trim()
|
||||
emails = _.select emails, (email)->
|
||||
email.indexOf("@") != -1
|
||||
return emails
|
||||
|
||||
$scope.registerUsers = () ->
|
||||
emails = parseEmails($scope.inputs.emails)
|
||||
$scope.error = false
|
||||
for email in emails
|
||||
queuedHttp
|
||||
.post("/admin/register", {
|
||||
email: email,
|
||||
_csrf: window.csrfToken
|
||||
})
|
||||
.success (user) ->
|
||||
$scope.users.push user
|
||||
$scope.inputs.emails = ""
|
||||
.error () ->
|
||||
$scope.error = true
|
|
@ -13,25 +13,11 @@ describe "Email Templator ", ->
|
|||
|
||||
beforeEach ->
|
||||
|
||||
@settings = {}
|
||||
@settings = appName: "testApp"
|
||||
@EmailBuilder = SandboxedModule.require modulePath, requires:
|
||||
"settings-sharelatex":@settings
|
||||
"logger-sharelatex": log:->
|
||||
|
||||
describe "welcomeEmail", ->
|
||||
|
||||
beforeEach ->
|
||||
@opts =
|
||||
to:"bob@bob.com"
|
||||
first_name:"bob"
|
||||
@email = @EmailBuilder.buildEmail("welcome", @opts)
|
||||
|
||||
it "should insert the first_name into the template", ->
|
||||
@email.html.indexOf(@opts.first_name).should.not.equal -1
|
||||
|
||||
it "should not have undefined in it", ->
|
||||
@email.html.indexOf("undefined").should.equal -1
|
||||
|
||||
describe "projectSharedWithYou", ->
|
||||
beforeEach ->
|
||||
@opts =
|
||||
|
|
|
@ -48,6 +48,12 @@ describe "PasswordResetTokenHandler", ->
|
|||
err.should.exist
|
||||
done()
|
||||
|
||||
it "should allow the expiry time to be overridden", (done) ->
|
||||
@redisMulti.exec.callsArgWith(0)
|
||||
@ttl = 42
|
||||
@PasswordResetTokenHandler.getNewToken @user_id, {expiresIn: @ttl}, (err, token) =>
|
||||
@redisMulti.expire.calledWith("password_token:#{@stubbedToken.toString("hex")}", @ttl).should.equal true
|
||||
done()
|
||||
|
||||
describe "getUserIdFromTokenAndExpire", ->
|
||||
|
||||
|
|
|
@ -40,6 +40,12 @@ describe "UserController", ->
|
|||
autoAllocate:sinon.stub()
|
||||
@UserUpdater =
|
||||
changeEmailAddress:sinon.stub()
|
||||
@EmailHandler =
|
||||
sendEmail:sinon.stub().callsArgWith(2)
|
||||
@PasswordResetTokenHandler =
|
||||
getNewToken: sinon.stub()
|
||||
@settings =
|
||||
siteUrl: "sharelatex.example.com"
|
||||
@UserController = SandboxedModule.require modulePath, requires:
|
||||
"./UserLocator": @UserLocator
|
||||
"./UserDeleter": @UserDeleter
|
||||
|
@ -51,6 +57,10 @@ describe "UserController", ->
|
|||
"../Authentication/AuthenticationManager": @AuthenticationManager
|
||||
"../Referal/ReferalAllocator":@ReferalAllocator
|
||||
"../Subscription/SubscriptionDomainAllocator":@SubscriptionDomainAllocator
|
||||
"../Email/EmailHandler": @EmailHandler
|
||||
"../PasswordReset/PasswordResetTokenHandler": @PasswordResetTokenHandler
|
||||
"crypto": @crypto = {}
|
||||
"settings-sharelatex": @settings
|
||||
"logger-sharelatex": {log:->}
|
||||
|
||||
|
||||
|
@ -60,7 +70,9 @@ describe "UserController", ->
|
|||
user :
|
||||
_id : @user_id
|
||||
body:{}
|
||||
@res = {}
|
||||
@res =
|
||||
send: sinon.stub()
|
||||
json: sinon.stub()
|
||||
@next = sinon.stub()
|
||||
describe "deleteUser", ->
|
||||
|
||||
|
@ -162,69 +174,52 @@ describe "UserController", ->
|
|||
|
||||
|
||||
describe "register", ->
|
||||
beforeEach ->
|
||||
@req.body.email = @user.email = "email@example.com"
|
||||
@crypto.randomBytes = sinon.stub().returns({toString: () => @password = "mock-password"})
|
||||
@PasswordResetTokenHandler.getNewToken.callsArgWith(2, null, @token = "mock-token")
|
||||
|
||||
it "should ask the UserRegistrationHandler to register user", (done)->
|
||||
describe "with a new user", ->
|
||||
beforeEach ->
|
||||
@UserRegistrationHandler.registerNewUser.callsArgWith(1, null, @user)
|
||||
@res.send = =>
|
||||
@UserRegistrationHandler.registerNewUser.calledWith(@req.body).should.equal true
|
||||
done()
|
||||
@UserController.register @req, @res
|
||||
|
||||
it "should try and log the user in if there is an EmailAlreadyRegisterd error", (done)->
|
||||
it "should ask the UserRegistrationHandler to register user", ->
|
||||
@UserRegistrationHandler.registerNewUser
|
||||
.calledWith({
|
||||
email: @req.body.email
|
||||
password: @password
|
||||
}).should.equal true
|
||||
|
||||
@UserRegistrationHandler.registerNewUser.callsArgWith(1, "EmailAlreadyRegisterd")
|
||||
@AuthenticationController.login = (req, res)=>
|
||||
assert.deepEqual req, @req
|
||||
assert.deepEqual res, @res
|
||||
done()
|
||||
@UserController.register @req, @res
|
||||
|
||||
it "should put the user on the session and mark them as justRegistered", (done)->
|
||||
@UserRegistrationHandler.registerNewUser.callsArgWith(1, null, @user)
|
||||
@res.send = =>
|
||||
@AuthenticationController.establishUserSession
|
||||
.calledWith(@req, @user)
|
||||
it "should generate a new password reset token", ->
|
||||
@PasswordResetTokenHandler.getNewToken
|
||||
.calledWith(@user_id, expiresIn: 7 * 24 * 60 * 60)
|
||||
.should.equal true
|
||||
assert.equal @req.session.justRegistered, true
|
||||
done()
|
||||
@UserController.register @req, @res
|
||||
|
||||
it "should redirect to project page", (done)->
|
||||
@UserRegistrationHandler.registerNewUser.callsArgWith(1, null, @user)
|
||||
@res.send = (opts)=>
|
||||
opts.redir.should.equal "/project"
|
||||
done()
|
||||
@UserController.register @req, @res
|
||||
|
||||
|
||||
it "should redirect passed redir if it exists", (done)->
|
||||
@UserRegistrationHandler.registerNewUser.callsArgWith(1, null, @user)
|
||||
@req.body.redir = "/somewhere"
|
||||
@res.send = (opts)=>
|
||||
opts.redir.should.equal "/somewhere"
|
||||
done()
|
||||
@UserController.register @req, @res
|
||||
|
||||
it "should allocate the referals", (done)->
|
||||
@req.session =
|
||||
referal_id : "23123"
|
||||
referal_source : "email"
|
||||
referal_medium : "bob"
|
||||
|
||||
@UserRegistrationHandler.registerNewUser.callsArgWith(1, null, @user)
|
||||
@req.body.redir = "/somewhere"
|
||||
@res.send = (opts)=>
|
||||
@ReferalAllocator.allocate.calledWith(@req.session.referal_id, @user._id, @req.session.referal_source, @req.session.referal_medium).should.equal true
|
||||
done()
|
||||
@UserController.register @req, @res
|
||||
|
||||
it "should auto allocate the subscription for that domain", (done)->
|
||||
@UserRegistrationHandler.registerNewUser.callsArgWith(1, null, @user)
|
||||
@res.send = (opts)=>
|
||||
@SubscriptionDomainAllocator.autoAllocate.calledWith(@user).should.equal true
|
||||
done()
|
||||
|
||||
it "should send a registered email", ->
|
||||
@EmailHandler.sendEmail
|
||||
.calledWith("registered", {
|
||||
to: @user.email
|
||||
setNewPasswordUrl: "#{@settings.siteUrl}/user/password/set?passwordResetToken=#{@token}"
|
||||
})
|
||||
.should.equal true
|
||||
|
||||
it "should return the user", ->
|
||||
@res.json
|
||||
.calledWith({
|
||||
email: @user.email
|
||||
setNewPasswordUrl: "#{@settings.siteUrl}/user/password/set?passwordResetToken=#{@token}"
|
||||
})
|
||||
.should.equal true
|
||||
|
||||
describe "with a user that already exists", ->
|
||||
beforeEach ->
|
||||
@UserRegistrationHandler.registerNewUser.callsArgWith(1, new Error("EmailAlreadyRegistered"), @user)
|
||||
@UserController.register @req, @res
|
||||
|
||||
it "should still generate a new password token and email", ->
|
||||
@PasswordResetTokenHandler.getNewToken.called.should.equal true
|
||||
@EmailHandler.sendEmail.called.should.equal true
|
||||
|
||||
describe "changePassword", ->
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ describe "UserDeleter", ->
|
|||
"../Newsletter/NewsletterManager": @NewsletterManager
|
||||
"../Subscription/SubscriptionHandler": @SubscriptionHandler
|
||||
"../Project/ProjectDeleter": @ProjectDeleter
|
||||
"logger-sharelatex": @logger = { log: sinon.stub() }
|
||||
|
||||
describe "deleteUser", ->
|
||||
|
||||
|
|
|
@ -20,14 +20,12 @@ describe "UserRegistrationHandler", ->
|
|||
setUserPassword: sinon.stub().callsArgWith(2)
|
||||
@NewsLetterManager =
|
||||
subscribe: sinon.stub().callsArgWith(1)
|
||||
@EmailHandler =
|
||||
sendEmail:sinon.stub().callsArgWith(2)
|
||||
@handler = SandboxedModule.require modulePath, requires:
|
||||
"../../models/User": {User:@User}
|
||||
"./UserCreator": @UserCreator
|
||||
"../Authentication/AuthenticationManager":@AuthenticationManager
|
||||
"../Newsletter/NewsletterManager":@NewsLetterManager
|
||||
"../Email/EmailHandler": @EmailHandler
|
||||
"logger-sharelatex": @logger = { log: sinon.stub() }
|
||||
|
||||
@passingRequest = {email:"something@email.com", password:"123"}
|
||||
|
||||
|
@ -87,9 +85,10 @@ describe "UserRegistrationHandler", ->
|
|||
done()
|
||||
|
||||
it "should return email registered in the error if there is a non holdingAccount there", (done)->
|
||||
@User.findOne.callsArgWith(1, null, {holdingAccount:false})
|
||||
@handler.registerNewUser @passingRequest, (err)=>
|
||||
err.should.equal "EmailAlreadyRegisterd"
|
||||
@User.findOne.callsArgWith(1, null, @user = {holdingAccount:false})
|
||||
@handler.registerNewUser @passingRequest, (err, user)=>
|
||||
err.should.deep.equal new Error("EmailAlreadyRegistered")
|
||||
user.should.deep.equal @user
|
||||
done()
|
||||
|
||||
describe "validRequest", ->
|
||||
|
@ -125,11 +124,6 @@ describe "UserRegistrationHandler", ->
|
|||
@NewsLetterManager.subscribe.calledWith(@user).should.equal true
|
||||
done()
|
||||
|
||||
it "should send a welcome email", (done)->
|
||||
@handler.registerNewUser @passingRequest, (err)=>
|
||||
@EmailHandler.sendEmail.calledWith("welcome").should.equal true
|
||||
done()
|
||||
|
||||
|
||||
it "should call the ReferalAllocator", (done)->
|
||||
done()
|
||||
|
|
Loading…
Reference in a new issue