mirror of
https://git.unistra.fr/aius/root/ldap-overleaf-sl.git
synced 2025-05-04 11:45:26 +02:00
fix external email registration, Admin. Working solution.
This commit is contained in:
parent
13b76926ca
commit
baccac37d5
11 changed files with 271 additions and 52 deletions
21
README.md
21
README.md
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
3
ldap-overleaf-sl/nginx/nginx-reload.sh
Normal file
3
ldap-overleaf-sl/nginx/nginx-reload.sh
Normal file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
/etc/init.d/nginx reload
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
57
ldap-overleaf-sl/sharelatex/admin-index.pug
Normal file
57
ldap-overleaf-sl/sharelatex/admin-index.pug
Normal 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 }}
|
|
@ -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
|
||||
|
|
|
@ -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") I have left, purged or imported my projects on Overleaf v1 (if any)
|
||||
|
||||
div.confirmation-checkbox-wrapper
|
||||
input(
|
||||
type="checkbox"
|
||||
ng-model="state.confirmSharelatexDelete"
|
||||
ng-change="checkValidation()"
|
||||
).pull-left
|
||||
label(style="display: inline") 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 || {})}
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Reference in a new issue