feat(ldap): allow resetting ldap password from user settings

currently, there is no way to reset password for those logging in
through ldap. i understand that this shouldn't really be handled by
erpnext, but there are people that have requested resetting the ldap
password from with the instance itself, and hence, we'll let that happen
now.

Signed-off-by: Chinmay D. Pai <chinmaydpai@gmail.com>
This commit is contained in:
Chinmay D. Pai 2020-03-19 21:44:45 +05:30
parent b7b27590e6
commit 94b2d856fc
No known key found for this signature in database
GPG key ID: 75507BE256F40CED
2 changed files with 81 additions and 3 deletions

View file

@ -97,6 +97,48 @@ frappe.ui.form.on('User', {
});
}, __("Password"));
frappe.db.get_single_value("LDAP Settings", "enabled").then((value) => {
if (value === 1 && frm.name != "Administrator") {
frm.add_custom_button(__("Reset LDAP Password"), function(){
const d = new frappe.ui.Dialog({
title: __("Reset LDAP Password"),
fields: [
{
label: __("New Password"),
fieldtype: "Password",
fieldname: "new_password",
reqd: 1
},
{
label: __("Confirm New Password"),
fieldtype: "Password",
fieldname: "confirm_password",
reqd: 1
},
{
label: __("Logout All Sessions"),
fieldtype: "Check",
fieldname: "logout_sessions"
}
],
primary_action: (values) => {
d.hide();
if(values.new_password !== values.confirm_password) {
frappe.throw(__("Passwords do not match!"));
}
frappe.call(
"frappe.integrations.doctype.ldap_settings.ldap_settings.reset_password", {
user: frm.doc.email,
password: values.new_password,
logout: values.logout_sessions
}).then(() => done());
}
});
d.show();
}, __("Password"));
}
});
frm.add_custom_button(__("Reset OTP Secret"), function() {
frappe.call({
method: "frappe.core.doctype.user.user.reset_otp_secret",

View file

@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe import _, safe_encode
from frappe.model.document import Document
@ -19,7 +19,7 @@ class LDAPSettings(Document):
else:
frappe.throw(_("LDAP Search String needs to end with a placeholder, eg sAMAccountName={0}"))
def connect_to_ldap(self, base_dn, password):
def connect_to_ldap(self, base_dn, password, read_only=True):
try:
import ldap3
import ssl
@ -44,7 +44,7 @@ class LDAPSettings(Document):
user=base_dn,
password=password,
auto_bind=bind_type,
read_only=True,
read_only=read_only,
raise_exceptions=True)
return conn
@ -170,6 +170,34 @@ class LDAPSettings(Document):
else:
frappe.throw(_("Invalid username or password"))
def reset_password(self, user, password, logout_sessions=False):
from ldap3 import HASHED_SALTED_SHA, MODIFY_REPLACE
from ldap3.utils.hashed import hashed
search_filter = "({0}={1})".format(self.ldap_email_field, user)
conn = self.connect_to_ldap(self.base_dn, self.get_password(raise_exception=False),
read_only=False)
if conn.search(
search_base=self.organizational_unit,
search_filter=search_filter,
attributes=self.get_ldap_attributes()
):
if conn.entries and conn.entries[0]:
entry_dn = conn.entries[0].entry_dn
hashed_password = hashed(HASHED_SALTED_SHA, safe_encode(password))
changes = {'userPassword': [(MODIFY_REPLACE, [hashed_password])]}
if conn.modify(entry_dn, changes=changes):
if logout_sessions:
from frappe.sessions import clear_sessions
clear_sessions(user=user, force=True)
frappe.msgprint(_("Password changed successfully."))
else:
frappe.throw(_("Failed to change password."))
else:
frappe.throw(_("LDAP User does not exist!"))
def convert_ldap_entry_to_dict(self, user_entry):
# support multiple email values
@ -211,3 +239,11 @@ def login():
# because of a GET request!
frappe.db.commit()
@frappe.whitelist()
def reset_password(user, password, logout):
ldap = frappe.get_doc("LDAP Settings")
if not ldap.enabled:
frappe.throw(_("LDAP is not enabled."))
ldap.reset_password(user, password, logout_sessions=int(logout))