mirror of
https://git.unistra.fr/aius/root/ldap-overleaf-sl.git
synced 2025-05-04 19:55: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}});
|
db.users.update({email : OLDEMAIL},{$set: { email : NEWEMAIL}});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Coming soon
|
|
||||||
|
|
||||||
- Option that Admins can invite non LDAP User
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
### Domain 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
|
# 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)'
|
# 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_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
|
### 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.
|
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
|
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:
|
if you want to enable this function set:
|
||||||
```
|
```
|
||||||
LDAP_CONTACTS: 'true'
|
LDAP_CONTACTS: 'true'
|
||||||
|
|
|
@ -48,9 +48,17 @@ services:
|
||||||
LDAP_BINDDN: ou=someunit,ou=people,dc=DOMAIN,dc=TLS
|
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
|
# 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=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)'
|
#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'
|
LDAP_CONTACTS: 'false'
|
||||||
|
|
||||||
# Same property, unfortunately with different names in
|
# Same property, unfortunately with different names in
|
||||||
|
|
|
@ -13,17 +13,20 @@ RUN npm install ldapts
|
||||||
|
|
||||||
# overwrite some files
|
# overwrite some files
|
||||||
COPY sharelatex/AuthenticationManager.js /var/www/sharelatex/web/app/src/Features/Authentication/
|
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/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/login.pug /var/www/sharelatex/web/app/views/user/
|
||||||
COPY sharelatex/settings.pug /var/www/sharelatex/web/app/views/user/settings.pug
|
COPY sharelatex/settings.pug /var/www/sharelatex/web/app/views/user/
|
||||||
COPY sharelatex/navbar.pug /var/www/sharelatex/web/app/views/layout/navbar.pug
|
COPY sharelatex/navbar.pug /var/www/sharelatex/web/app/views/layout/
|
||||||
COPY sharelatex/share.pug /var/www/sharelatex/web/app/views/project/editor/share.pug
|
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)
|
### 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 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
|
RUN touch /var/www/sharelatex/web/app/views/project/editor/review-panel.pug
|
||||||
|
|
||||||
|
|
||||||
### Nginx and Certificates
|
### Nginx and Certificates
|
||||||
# enable https via letsencrypt
|
# enable https via letsencrypt
|
||||||
RUN rm /etc/nginx/sites-enabled/sharelatex.conf
|
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
|
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
|
# reload nginx via cron for reneweing https certificates automatically
|
||||||
COPY nginx/nginx-reload.cron /etc/cron.d/nginx-cron
|
COPY nginx/nginx-reload.sh /etc/cron.weekly/
|
||||||
RUN chmod 0744 /etc/cron.d/nginx-cron
|
RUN chmod 0744 /etc/cron.weekly/nginx-reload.sh
|
||||||
RUN touch /var/log/cron.log
|
|
||||||
RUN crontab /etc/cron.d/nginx-cron
|
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
authUserObj(error, user, query, password, callback) {
|
||||||
// check if user is in ldap and logon if the ldapuser exists
|
if ( process.env.ALLOW_EMAIL_LOGIN ) {
|
||||||
// external email login
|
// (external) email login
|
||||||
if (user && user.hashedPassword) {
|
if (user && user.hashedPassword) {
|
||||||
console.log("email login for existing user")
|
console.log("email login for existing user")
|
||||||
bcrypt.compare(password, user.hashedPassword, function (error, match) {
|
// check passwd against local db
|
||||||
if (match) {
|
bcrypt.compare(password, user.hashedPassword, function (error, match) {
|
||||||
console.log("Fine")
|
if (match) {
|
||||||
AuthenticationManager.login(user, password, callback)
|
console.log("Fine")
|
||||||
}
|
AuthenticationManager.login(user, password, callback)
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// check passwd against ldap
|
||||||
|
AuthenticationManager.ldapAuth(query, password, AuthenticationManager.createIfNotExistAndLogin, callback, user)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// No local passwd check user has to be in ldap and use ldap credentials
|
||||||
AuthenticationManager.ldapAuth(query, password, AuthenticationManager.createIfNotExistAndLogin, callback, user)
|
AuthenticationManager.ldapAuth(query, password, AuthenticationManager.createIfNotExistAndLogin, callback, user)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
|
|
@ -89,6 +89,7 @@ module.exports = ContactsController = {
|
||||||
const ldap_base = process.env.LDAP_BASE
|
const ldap_base = process.env.LDAP_BASE
|
||||||
// get user data
|
// get user data
|
||||||
try {
|
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 ,});
|
const {searchEntries,searchReferences,} = await client.search(ldap_base, {scope: 'sub',filter: process.env.LDAP_GROUP_FILTER ,});
|
||||||
await searchEntries;
|
await searchEntries;
|
||||||
for (var i = 0; i < searchEntries.length; i++) {
|
for (var i = 0; i < searchEntries.length; i++) {
|
||||||
|
@ -99,7 +100,10 @@ module.exports = ContactsController = {
|
||||||
entry['first_name'] = obj['givenName']
|
entry['first_name'] = obj['givenName']
|
||||||
entry['last_name'] = obj['sn']
|
entry['last_name'] = obj['sn']
|
||||||
entry['type'] = "user"
|
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) {
|
} catch (ex) {
|
||||||
console.log(String(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
|
ul.nav.navbar-nav.navbar-right
|
||||||
if (getSessionUser() && getSessionUser().isAdmin)
|
if (getSessionUser() && getSessionUser().isAdmin)
|
||||||
li.dropdown(class="subdued", dropdown)
|
li
|
||||||
a.dropdown-toggle(href, dropdown-toggle)
|
a(href="/admin") Admin
|
||||||
| Admin
|
|
||||||
b.caret
|
|
||||||
ul.dropdown-menu
|
|
||||||
li
|
|
||||||
a(href="/admin") Manage Site
|
|
||||||
li
|
|
||||||
a(href="/admin/user") Manage Users
|
|
||||||
|
|
||||||
|
|
||||||
// loop over header_extras
|
// loop over header_extras
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
extends ../layout
|
extends ../layout
|
||||||
|
|
||||||
block content
|
block content
|
||||||
.content.content-alt
|
.content.content-alt
|
||||||
.container
|
.container
|
||||||
|
@ -12,19 +13,166 @@ block content
|
||||||
h1 #{translate("account_settings")}
|
h1 #{translate("account_settings")}
|
||||||
.account-settings(ng-controller="AccountSettingsController", ng-cloak)
|
.account-settings(ng-controller="AccountSettingsController", ng-cloak)
|
||||||
|
|
||||||
|
if hasFeature('affiliations')
|
||||||
|
include settings/user-affiliations
|
||||||
|
|
||||||
.row
|
.row
|
||||||
.col-md-5
|
.col-md-5
|
||||||
h3
|
h3 #{translate("update_account_info")}
|
||||||
| #{translate("sessions")}
|
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
|
if shouldAllowEditingDetails
|
||||||
a(id="sessions-link", href="/user/sessions") #{translate("manage_sessions")}
|
.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
|
hr
|
||||||
|
|
||||||
h3
|
h3
|
||||||
| Contact
|
| Contact
|
||||||
div
|
div
|
||||||
| If you need any help, please contact your sysadmins.
|
| 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
|
.form-group
|
||||||
tags-input(
|
tags-input(
|
||||||
template="shareTagTemplate"
|
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"
|
ng-model="inputs.contacts"
|
||||||
focus-on="open"
|
focus-on="open"
|
||||||
display-property="display"
|
display-property="display"
|
||||||
|
|
Loading…
Add table
Reference in a new issue