fix external email registration, Admin. Working solution.

This commit is contained in:
Simon M. Haller-Seeber 2020-05-15 12:45:34 +02:00
parent 13b76926ca
commit baccac37d5
11 changed files with 271 additions and 52 deletions

View File

@ -34,12 +34,6 @@ db.users.find({email:"EMAIL"}).pretty()
db.users.update({email : OLDEMAIL},{$set: { email : NEWEMAIL}});
```
## Coming soon
- Option that Admins can invite non LDAP User
## Configuration
### Domain Configuration
@ -74,15 +68,24 @@ LDAP_BINDDN: ou=someunit,ou=people,dc=DOMAIN,dc=TLS
# By default tries to bind directly with the ldap user - this user has to be in the LDAP GROUP
# you have to set a group filter a minimal groupfilter would be: '(objectClass=person)'
LDAP_GROUP_FILTER: '(memberof=GROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)'
LDAP_CONTACTS: 'true'
# If user is in ADMIN_GROUP on user creation (first login) isAdmin is set to true.
# Admin Users can invite external (non ldap) users. This feature makes only sense
# when ALLOW_EMAIL_LOGIN is set to 'true'. Additionally adminsy can send
# system wide messages.
#LDAP_ADMIN_GROUP_FILTER: '(memberof=cn=ADMINGROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)'
ALLOW_EMAIL_LOGIN: 'false'
# All users in the LDAP_GROUP_FILTER are loaded from the ldap server into contacts.
LDAP_CONTACTS: 'false'
```
### LDAP Contacts
If you enable this, then all users in GROUPNAME are loaded from the ldap server into the contacts.
If you enable this, then all users in LDAP_GROUP_FILTER are loaded from the ldap server into the contacts.
At the moment this happens every time you click on "Share" within a project.
The user search happens without bind - so if your LDAP needs a bind you can adapt this in the
function `getLdapContacts()` in ContactsController.js (lines 82 - 107)
function `getLdapContacts()` in ContactsController.js (lines 92)
if you want to enable this function set:
```
LDAP_CONTACTS: 'true'

View File

@ -48,9 +48,17 @@ services:
LDAP_BINDDN: ou=someunit,ou=people,dc=DOMAIN,dc=TLS
# By default tries to bind directly with the ldap user - this user has to be in the LDAP GROUP
LDAP_GROUP_FILTER: '(memberof=cn=GROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)'
#LDAP_GROUP_FILTER: '(memberof=GROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)'
# if user is in ADMIN_GROUP on user creation (first login) isAdmin is set to true.
# If user is in ADMIN_GROUP on user creation (first login) isAdmin is set to true.
# Admin Users can invite external (non ldap) users. This feature makes only sense
# when ALLOW_EMAIL_LOGIN is set to 'true'. Additionally adminsy can send
# system wide messages.
#LDAP_ADMIN_GROUP_FILTER: '(memberof=cn=ADMINGROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)'
ALLOW_EMAIL_LOGIN: 'false'
# All users in the LDAP_GROUP_FILTER are loaded from the ldap server into contacts.
# This LDAP search happens without bind. If you want this and your LDAP needs a bind you can
# adapt this in the function getLdapContacts() in ContactsController.js (lines 82 - 107)
LDAP_CONTACTS: 'false'
# Same property, unfortunately with different names in

View File

@ -13,17 +13,20 @@ RUN npm install ldapts
# overwrite some files
COPY sharelatex/AuthenticationManager.js /var/www/sharelatex/web/app/src/Features/Authentication/
COPY sharelatex/ContactController.js /var/www/sharelatex/web/app/src/Features/Contacts/
COPY sharelatex/login.pug /var/www/sharelatex/web/app/views/user/login.pug
COPY sharelatex/settings.pug /var/www/sharelatex/web/app/views/user/settings.pug
COPY sharelatex/navbar.pug /var/www/sharelatex/web/app/views/layout/navbar.pug
COPY sharelatex/share.pug /var/www/sharelatex/web/app/views/project/editor/share.pug
COPY sharelatex/ContactController.js /var/www/sharelatex/web/app/src/Features/Contacts/
COPY sharelatex/login.pug /var/www/sharelatex/web/app/views/user/
COPY sharelatex/settings.pug /var/www/sharelatex/web/app/views/user/
COPY sharelatex/navbar.pug /var/www/sharelatex/web/app/views/layout/
COPY sharelatex/share.pug /var/www/sharelatex/web/app/views/project/editor/
# Non LDAP User Reegistration for Admins
COPY sharelatex/admin-index.pug /var/www/sharelatex/web/app/views/admin/index.pug
RUN rm /var/www/sharelatex/web/app/views/admin/register.pug
### To remove comments entirly (bug https://github.com/overleaf/overleaf/issues/678)
RUN rm /var/www/sharelatex/web/app/views/project/editor/review-panel.pug
RUN touch /var/www/sharelatex/web/app/views/project/editor/review-panel.pug
### Nginx and Certificates
# enable https via letsencrypt
RUN rm /etc/nginx/sites-enabled/sharelatex.conf
@ -34,8 +37,6 @@ RUN wget https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/
RUN wget https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem -O /etc/nginx/ssl-dhparams.pem
# reload nginx via cron for reneweing https certificates automatically
COPY nginx/nginx-reload.cron /etc/cron.d/nginx-cron
RUN chmod 0744 /etc/cron.d/nginx-cron
RUN touch /var/log/cron.log
RUN crontab /etc/cron.d/nginx-cron
COPY nginx/nginx-reload.sh /etc/cron.weekly/
RUN chmod 0744 /etc/cron.weekly/nginx-reload.sh

View File

@ -1,4 +0,0 @@
* 2 * * * root /etc/init.d/nginx reload
#* * * * * root sleep 10; echo "Nginx relaoded" >> /var/log/cron.log 2>&1
# Reload Nginx to reload the certificates (2am)

View File

@ -0,0 +1,3 @@
#!/bin/bash
/etc/init.d/nginx reload

View File

@ -88,17 +88,23 @@ const AuthenticationManager = {
},
authUserObj(error, user, query, password, callback) {
// check if user is in ldap and logon if the ldapuser exists
// external email login
if (user && user.hashedPassword) {
console.log("email login for existing user")
bcrypt.compare(password, user.hashedPassword, function (error, match) {
if (match) {
console.log("Fine")
AuthenticationManager.login(user, password, callback)
}
})
if ( process.env.ALLOW_EMAIL_LOGIN ) {
// (external) email login
if (user && user.hashedPassword) {
console.log("email login for existing user")
// check passwd against local db
bcrypt.compare(password, user.hashedPassword, function (error, match) {
if (match) {
console.log("Fine")
AuthenticationManager.login(user, password, callback)
}
})
} else {
// check passwd against ldap
AuthenticationManager.ldapAuth(query, password, AuthenticationManager.createIfNotExistAndLogin, callback, user)
}
} else {
// No local passwd check user has to be in ldap and use ldap credentials
AuthenticationManager.ldapAuth(query, password, AuthenticationManager.createIfNotExistAndLogin, callback, user)
}
return null

View File

@ -89,6 +89,7 @@ module.exports = ContactsController = {
const ldap_base = process.env.LDAP_BASE
// get user data
try {
// if you need an client.bind do it here.
const {searchEntries,searchReferences,} = await client.search(ldap_base, {scope: 'sub',filter: process.env.LDAP_GROUP_FILTER ,});
await searchEntries;
for (var i = 0; i < searchEntries.length; i++) {
@ -99,7 +100,10 @@ module.exports = ContactsController = {
entry['first_name'] = obj['givenName']
entry['last_name'] = obj['sn']
entry['type'] = "user"
contacts.push(entry)
// Only add to contacts if entry is not there.
if(contacts.indexOf(entry) === -1) {
contacts.push(entry);
}
}
} catch (ex) {
console.log(String(ex))

View File

@ -0,0 +1,57 @@
extends ../layout
block content
.content.content-alt
.container
.row
.col-xs-12
.card(ng-controller="RegisterUsersController")
.page-header
h1 Admin Panel
tabset(ng-cloak)
tab(heading="System Messages")
each message in systemMessages
.alert.alert-info.row-spaced(ng-non-bindable) #{message.content}
hr
form(method='post', action='/admin/messages')
input(name="_csrf", type="hidden", value=csrfToken)
.form-group
label(for="content")
input.form-control(name="content", type="text", placeholder="Message...", required)
button.btn.btn-primary(type="submit") Post Message
hr
form(method='post', action='/admin/messages/clear')
input(name="_csrf", type="hidden", value=csrfToken)
button.btn.btn-danger(type="submit") Clear all messages
tab(heading="Register non LDAP User")
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 }}

View File

@ -14,15 +14,8 @@ nav.navbar.navbar-default.navbar-main
ul.nav.navbar-nav.navbar-right
if (getSessionUser() && getSessionUser().isAdmin)
li.dropdown(class="subdued", dropdown)
a.dropdown-toggle(href, dropdown-toggle)
| Admin
b.caret
ul.dropdown-menu
li
a(href="/admin") Manage Site
li
a(href="/admin/user") Manage Users
li
a(href="/admin") Admin
// loop over header_extras

View File

@ -1,4 +1,5 @@
extends ../layout
block content
.content.content-alt
.container
@ -12,19 +13,166 @@ block content
h1 #{translate("account_settings")}
.account-settings(ng-controller="AccountSettingsController", ng-cloak)
if hasFeature('affiliations')
include settings/user-affiliations
.row
.col-md-5
h3
| #{translate("sessions")}
h3 #{translate("update_account_info")}
form(async-form="settings", name="settingsForm", method="POST", action="/user/settings", novalidate)
input(type="hidden", name="_csrf", value=csrfToken)
if !hasFeature('affiliations')
// show the email, non-editable
.form-group
label.control-label #{translate("email")}
div.form-control(
readonly="true",
ng-non-bindable
) #{user.email}
div
a(id="sessions-link", href="/user/sessions") #{translate("manage_sessions")}
if shouldAllowEditingDetails
.form-group
label(for='firstName').control-label #{translate("first_name")}
input.form-control(
id="firstName"
type='text',
name='first_name',
value=user.first_name
ng-non-bindable
)
.form-group
label(for='lastName').control-label #{translate("last_name")}
input.form-control(
id="lastName"
type='text',
name='last_name',
value=user.last_name
ng-non-bindable
)
.form-group
form-messages(aria-live="polite" for="settingsForm")
.alert.alert-success(ng-show="settingsForm.response.success")
| #{translate("thanks_settings_updated")}
.actions
button.btn.btn-primary(
type='submit',
ng-disabled="settingsForm.$invalid"
) #{translate("update")}
else
.form-group
label.control-label #{translate("first_name")}
div.form-control(
readonly="true",
ng-non-bindable
) #{user.first_name}
.form-group
label.control-label #{translate("last_name")}
div.form-control(
readonly="true",
ng-non-bindable
) #{user.last_name}
.col-md-5.col-md-offset-1
h3
| Set Password for Email login
p
| Note: you can not change the LDAP password from here. You can set/reset a password for
| your email login:
| #[a(href="/user/password/reset", target='_blank') Reset.]
| !{moduleIncludes("userSettings", locals)}
hr
h3
| Contact
div
| If you need any help, please contact your sysadmins.
if !externalAuthenticationSystemUsed() || (settings.createV1AccountOnLogin && settings.overleaf)
p #{translate("need_to_leave")}
a(href, ng-click="deleteAccount()") #{translate("delete_your_account")}
script(type='text/ng-template', id='deleteAccountModalTemplate')
.modal-header
h3 #{translate("delete_account")}
div.modal-body#delete-account-modal
p !{translate("delete_account_warning_message_3")}
if settings.createV1AccountOnLogin && settings.overleaf
p
strong
| Your Overleaf v2 projects will be deleted if you delete your account.
| If you want to remove any remaining Overleaf v1 projects in your account,
| please first make sure they are imported to Overleaf v2.
if settings.overleaf && !hasPassword
p
b
| #[a(href="/user/password/reset", target='_blank') #{translate("delete_acct_no_existing_pw")}].
else
form(novalidate, name="deleteAccountForm")
label #{translate('email')}
input.form-control(
type="text",
autocomplete="off",
placeholder="",
ng-model="state.deleteText",
focus-on="open",
ng-keyup="checkValidation()"
)
label #{translate('password')}
input.form-control(
type="password",
autocomplete="off",
placeholder="",
ng-model="state.password",
ng-keyup="checkValidation()"
)
div.confirmation-checkbox-wrapper
input(
type="checkbox"
ng-model="state.confirmV1Purge"
ng-change="checkValidation()"
).pull-left
label(style="display: inline") &nbsp;I have left, purged or imported my projects on Overleaf v1 (if any) &nbsp;
div.confirmation-checkbox-wrapper
input(
type="checkbox"
ng-model="state.confirmSharelatexDelete"
ng-change="checkValidation()"
).pull-left
label(style="display: inline") &nbsp;I understand this will delete all projects in my Overleaf v2 account (and ShareLaTeX account, if any) with email address #[em {{ userDefaultEmail }}]
div(ng-if="state.error")
div.alert.alert-danger(ng-switch="state.error.code")
span(ng-switch-when="InvalidCredentialsError")
| #{translate('email_or_password_wrong_try_again')}
span(ng-switch-when="SubscriptionAdminDeletionError")
| #{translate('subscription_admins_cannot_be_deleted')}
span(ng-switch-when="UserDeletionError")
| #{translate('user_deletion_error')}
span(ng-switch-default)
| #{translate('generic_something_went_wrong')}
if settings.createV1AccountOnLogin && settings.overleaf
div(ng-if="state.error && state.error.code == 'InvalidCredentialsError'")
div.alert.alert-info
| If you can't remember your password, or if you are using Single-Sign-On with another provider
| to sign in (such as Twitter or Google), please
| #[a(href="/user/password/reset", target='_blank') reset your password],
| and try again.
.modal-footer
button.btn.btn-default(
ng-click="cancel()"
) #{translate("cancel")}
button.btn.btn-danger(
ng-disabled="!state.isValid || state.inflight"
ng-click="delete()"
)
span(ng-hide="state.inflight") #{translate("delete")}
span(ng-show="state.inflight") #{translate("deleting")}...
script(type='text/javascript').
window.passwordStrengthOptions = !{StringHelper.stringifyJsonForScript(settings.passwordStrengthOptions || {})}

View File

@ -120,7 +120,7 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
.form-group
tags-input(
template="shareTagTemplate"
placeholder='Direct share with collaborators is enabled only for ldap users.'
placeholder='Direct share with collaborators is enabled only for activated users.'
ng-model="inputs.contacts"
focus-on="open"
display-property="display"