# This login plug-in authenticates using LDAP # 2010-12-30 - GMF - Customized for simple case (removed per-user filter) # 2013-04-12 - GMF - Moved to LogAnalysisInfo/default; added auto_update # 2014-04-15 - GMF - Fixed bug with users that don't exist yet in the users node, creating an empty user node. ldap = { expression = ` #echo("login plug-in ldap running"); # Hard-coded HDS preferences string admin_dn = preferences.security.ldap_administrator_dn; string admin_password = format(preferences.security.ldap_administrator_password, '!decrypt'); # Build the LDAP connection URL string url = if (preferences.security.use_ssl_for_ldap) then "ldaps" else "ldap"; url .= "://" . preferences.security.ldap_server_hostname . "/"; #echo("url: " . url); # Initialize LDAP string ld = ldap_initialize(url); #echo("ld: " . ld); # Bind to LDAP as administrator node info = new_node(); @info{"dn"} = admin_dn; @info{"password"} = admin_password; #echo("binding with info: " . node_as_string(info)); bool success = ldap_bind(ld, info); #echo("ADMIN BOUND: success=" . success); if (success == false) then error("LDAP admin login failed; failed to bind to admin dn '" . admin_dn . "' (" . volatile.ldap_error_message . " [" . volatile.ldap_error_return_code . "; " . volatile.ldap_errno . "; " . volatile.ldap_error_message_ret . ")"); # Find the login user in the directory node searchinfo = new_node(); @searchinfo{"base"} = preferences.security.ldap_base; @searchinfo{"scope"} = "subtree"; @searchinfo{"filter"} = "(" . preferences.security.ldap_username_label . "=" . username . ")"; #echo("searchinfo: " . node_as_string(searchinfo)); node attrs = new_node(); @attrs{0} = "distinguishedName"; node searchResult = ldap_search(ld, searchinfo, attrs); #echo("searchResult: " . node_as_string(searchResult)); # If the user doesn't exist, login fails if (num_subnodes(searchResult) == 0) then ( # echo("No LDAP record found with " . preferences.security.ldap_username_label . "=" . username); false; ); else ( # Get the login user's DN string login_user_dn = @searchResult{0}{'distinguishedName'}; #echo("login_user_dn: " . login_user_dn); # Bind to them, to check their password @info{"dn"} = login_user_dn; #echo("binding to login user DN: " . @info{"dn"}); @info{"password"} = password; #echo("Calling ldap_bind with info: " . node_as_string(info)); bool success = ldap_bind(ld, info); #echo("USER BIND: success=" . success); # If binding failed, login succeeded; make sure a user record exists if (success) then ( string converted_username = lowercase(username); converted_username = replace_all(converted_username, ' ', '_'); # Create the user node if it doesn't exist # 2014-04-15 - GMF - Don't do this; it creates an empty node if converted_username does not match the user's node name. Instead, look up the node where username matches converted_username (which might have a different nodename than the username), and use that. # if (!('users'?{converted_username})) then # @'users'{converted_username} = ''; # Find the user node node user; node useri; bool founduser = false; foreach useri 'users' ( if (@useri{"username"} eq converted_username) then ( user = useri; founduser = true; ); ); # If a node doesn't exist with this username, create one if (!founduser) then user = 'users'{converted_username}; # echo("user: " . node_as_string(user)); # Set the username and password checksum @user{"username"} = username; @user{"password_checksum"} = md5_digest(password); # Make them a view-only user. To apply other roles for some users, modify this code. # (user . '.access.0.roles.role_2') = 'role_2'; # Give use access to all profiles (if selective profile access is desired, edit this code to set access.profiles to a list, e.g.: # # profiles = { # 0 = "profile1" # 1 = "profile2" # } # profiles # ). # (user . '.access.0.all_profiles') = true; # An example of an LDAP search is shown below. Such a search might be used to determine membership in an "admin" group, for instance, # or to decide which profiles they can access. But the default behavior of this plug-in is to make everyone a view-only user of all profile. # node searchinfo = new_node(); # @searchinfo{"base"} = base; # @searchinfo{"scope"} = "subtree"; # @searchinfo{"filter"} = "(CN=David Gilmore)"; # node searchResult = ldap_search(ld, searchinfo); # echo("searchResult: " . node_as_string(searchResult)); # # Run a search to find who reports to this user [FAKING IT RIGHT NOW WITH displayName] # node searchinfo = new_node(); # @searchinfo{"base"} = base; # @searchinfo{"scope"} = "subtree"; # @searchinfo{"filter"} = "(displayName=" . username . ")"; # echo("searchinfo: " . node_as_string(searchinfo)); # node searchResult = ldap_search(ld, searchinfo); # echo("reportsTo search result: " . node_as_string(searchResult)); # # # Build a filter for this user, which selects all the people reporting to them (as determined by the LDAP search above) # string report_filter = ""; # node result; # foreach result searchResult ( # if (report_filter ne "") then # report_filter .= " or "; # report_filter .= "(user within '" . @result{"name"} . "')"; # ); # echo("report_filter: " . report_filter); # # (user . '.report_filters.all_profiles.filter_expression') = report_filter; # # echo("modified user: " . node_as_string(user)); save_node('users'); # Authentication succeeded, so save true true; ); # if bind to use succeeded # If binding failed, login failed else ( # echo("LDAP login failed; failed to bind to admin dn '" . admin_dn . "' (" . volatile.ldap_error_message . " [" . volatile.ldap_error_return_code . "; " . volatile.ldap_errno . "; " . volatile.ldap_error_message_ret . ")"); false; ); ); # if user exists in LDAP ` # The auto_update node is used by the internal auto-update # login_plugins only, it has no other function. # The node prevents the auto-update code to automatically add # ldap.cfg as active plug-in to preferences.login_plugins. # auto-update runs only once to handle login_plugins which # existed prior version 8.6.2. auto_update = false } # ldap