ACC SHELL

Path : /usr/share/YaST2/clients/
File Upload :
Current File : //usr/share/YaST2/clients/ldap_browser.ycp

/**
 * File:	clients/ldap/ldap_browser.ycp
 * Package:	Configuration of LDAP
 * Summary:	Simple browser and editor of LDAP tree
 * Author:	Jiri Suchomel <jsuchome@suse.cz>
 *
 * $Id: ldap_browser.ycp 55580 2009-02-18 12:56:30Z jsuchome $
 *
 */
{
    import "CommandLine";
    import "Directory";
    import "FileUtils";
    import "Label";
    import "Ldap";
    import "LdapPopup";
    import "Popup";
    import "Wizard";

    include "ldap/routines.ycp";

    textdomain "ldap-client";

    map cmdline = $[
	"id"		: "ldap_browser",
	"mappings"	: $[
	]
    ];
    if (size (WFM::Args()) > 0)
    {
	return CommandLine::Run( cmdline );
    }

    string root_dn		= "";
    string current_dn		= "";
    map data			= $[];
    map<string, any> tmp_data	= $[];
    // map of already read subtrees
    map<string,boolean> dns	= $[];
    list<string> subdns		= [];
    list<term> tree_items	= [];
    map topdns			= $[];
    map open_items		= $[];

    string help_text	=
	// general help text for LDAP browser
	_("<p>Browse the LDAP tree in the left part of the dialog.</p>") +
	// help text for LDAP browser
	_("<p>Once the LDAP object is selected in the tree, the table shows the object data. Use <b>Edit</b> to change the value of the selected attribute. Use <b>Save</b> to save your changes to LDAP.</p>");

    // popup question (Continue/Cancel follows)
    string unsaved	= _("There are unsaved changes in the current entry.
Discard these changes?
");

    term contents = `HBox (
	`HWeight (1, `ReplacePoint (`id (`treeContents), `Top (`HBox ()))),
	`HWeight (1, `ReplacePoint (`id (`entryContents), `Top (`HBox ())))
    );
    map display_info	= UI::GetDisplayInfo ();
    boolean textmode	= display_info["TextMode"]:false;

    // helper: data modified?
    define boolean Modified () {
	return size (tmp_data) > 0;
    }


    // helper: create the value that should be shown instead of whole DN in tree
    define string show_dn (string dn) {
	if (topdns[dn]:false)
	    return dn;
	return get_rdn (dn);
    }

    // helper for set_tree_term function: create new items for subtrees
    define list<term> update_items (list<term> its) {

	return maplist (term it, its, {
	    string dn	= it[0,0]:"";
	    if (dn == current_dn)
	    {
		return `item (`id(dn), show_dn (dn), true,
		    maplist(string k, subdns, ``(
			`item (`id(k), show_dn (k), false, []))));
	    }
	    integer last = size (it) - 1;
	    if (size (it[last]:[]) == 0)
		return it;
	    // `OpenItems doesn't work in ncurses...
	    boolean open = haskey (open_items, dn) && !textmode;
	    return `item (
		`id(dn), show_dn (dn), open, update_items (it[last]:[]));
	});
    }

    // -----------------------------
    // create the term with LDAP tree
    define void set_tree_term () {

	term cont = `HBox (`VSpacing (20), `VBox (`HSpacing(70),
	    `VSpacing (0.2),
	    `HBox (
		`HSpacing (),
		`ReplacePoint (`id (`reptree), `Tree (`id(`tree), root_dn, [])),
		`ReplacePoint (`id (`repbuttons), `Empty ()),
		`HSpacing ()
	    ),
	    `HBox (
		`HSpacing (1.5),
		`HStretch (),
		textmode ?
		  // button label
		  `PushButton (`id (`open), `opt (`key_F6),_("&Open")):
		  `Empty (),
		// button label
		`PushButton (`id(`reload),`opt(`key_F8), _("&Reload")),
		`HSpacing (1.5)
	    ),
	    `VSpacing (0.6)
	));

	UI::ReplaceWidget (`treeContents, cont);

	if (size (tree_items) == 0)
	{
	    list<string> out	= (list<string>) SCR::Read (.ldap.search, $[
		"base_dn"	: root_dn,
		"scope"		: 1,
		"dn_only"	: true,
		"not_found_ok"	: true ]
	    );
	    if (size (out) > 0)
	    {
		tree_items = maplist (string dn, out, {
		    dns [dn]	= false;
		    topdns[dn]	= true;
		    return `item (`id(dn), dn, false, []);
		});
	    }
	}

	if (size (tree_items) > 0)
	{
	    UI::ReplaceWidget (`id (`reptree), textmode ?
		`Tree (`id(`tree), root_dn, tree_items) :
		`Tree (`id(`tree), `opt(`notify), root_dn, tree_items));
	    // no item is selected
	    UI::ChangeWidget (`tree, `CurrentItem, nil);
	}
	else if (root_dn == "")
	{
	    list bases = (list) SCR::Read (.ldap.search, $[
		"base_dn"	: "",
	        "scope"		: 0,
		"attrs"		: [ "namingContexts" ]
	    ]);
	    if (size (bases) > 0)
		tree_items = maplist (string dn, bases[0,"namingContexts"]:[], {
		    topdns[dn]	= true;
		    return `item(`id(dn), dn, false, []);
		});
	    if (size (tree_items) > 0)
	    {
		UI::ReplaceWidget (`id (`reptree), textmode ?
		    `Tree (`id(`tree), root_dn, tree_items) :
		    `Tree (`id(`tree), `opt(`notify), root_dn, tree_items));
		UI::ChangeWidget (`tree, `CurrentItem, nil);
	    }
	    if (size (topdns) == 1)
		root_dn	= bases[0,"namingContexts",0]:"";
	}

	if (textmode)
	    UI::SetFocus (`id(`tree));

	if (current_dn != "")
	    UI::ChangeWidget (`id(`tree), `CurrentItem, current_dn);
    }

    // -----------------------------
    // create the term with LDAP entry data table
    define void set_entry_term () {

        list items			= [];

	// generate table items from already existing values
        foreach (string attr, any val, (map<string,any>) data, {
	    if (is (val, map) || val == nil)
		return;
	    list<string> value = [];
	    if (is (val, list))
	    {
		value = (list<string>)val;
	    }
	    if (is (val, byteblock) ||
		(is (val, list) && is (value[0]:nil, byteblock)))
	    {
		y2warning ("binary value (%1) cannot be edited", attr);
		return;
	    }
	    else if (is (val, integer))
	    {
		value = [ sformat ("%1", val) ];
		data [attr] = value;
	    }
	    else if (is (val, string))
	    {
		value = [ (string)val ];
		data [attr] = value;
	    }
	    items = add (items,`item (`id(attr), attr, mergestring(value,",")));
	});

	// generate table items with empty values
	// (not set for this user/group yet)
	// we need to read available attributes from Ldap
	foreach (string class, (list<string>) sort (data["objectClass"]:[]), {
	    foreach (string at,(list<string>)Ldap::GetAllAttributes (class), {
		if (!haskey (data, at))
		{
		    data[at] = [];
		    items = add (items, `item (`id(at), at, ""));
		}
	    });
	});

	term cont = `HBox(`HSpacing (1.5), `VBox(
	    `Left (`Label (current_dn)),
	    `Table(`id(`table), `opt (`notify, `immediate), `header(
		// table header 1/2
		_("Attribute") + "  ",
		// table header 2/2
		_("Value")),
		items),
	    `HBox (
		`PushButton(`id(`edit), `opt(`key_F4), Label::EditButton()),
		`HStretch(),
		`PushButton(`id(`save), `opt(`key_F2), Label::SaveButton())
	    ),
	    `VSpacing (0.5)
	    ),
	    `HSpacing (1.5)
	);

	UI::ReplaceWidget (`entryContents, cont);

	if (size (items) == 0)
	    UI::ChangeWidget (`id(`edit), `Enabled, false);
	else
	    // no item is selected
	    UI::ChangeWidget (`table, `CurrentItem, nil);

	UI::ChangeWidget (`id (`edit), `Enabled, false);
	UI::ChangeWidget (`id (`save), `Enabled, false);
        UI::SetFocus (`id(`table));
    }

    Wizard::CreateDialog ();

    Wizard::SetDesktopIcon ("ldap_browser");
    // dialog caption
    Wizard::SetContentsButtons (_("LDAP Browser"),
	contents, help_text, "", Label::CloseButton());

    Wizard::HideBackButton ();
    Wizard::HideAbortButton ();

    // read current LDAP configuration
    Ldap::Read ();

    list configurations	= [];
    string configurations_file	= Directory::vardir + "/ldap_servers.ycp";
    // combobox item
    string default_name	= _("Current LDAP Client settings");
    map configuration	= $[
	"server"	: Ldap::GetFirstServer (Ldap::server),
	"bind_dn"	: Ldap::GetBindDN (),
	"ldap_tls"	: Ldap::ldap_tls,
	"name"		: default_name
    ];
    // read configuration of LDAP browser
    if (FileUtils::Exists (configurations_file))
    {
	configurations	= (list) SCR::Read (.target.ycp, configurations_file);
	if (configurations == nil || !is (configurations, list))
	    configurations	= [];
    }
    if (configurations == [])
    {
	configurations	= [ configuration ];
    }

    configuration	= configurations[0]:$[];

    // helper function: generate items for combo box
    list connection_items (string selected) {
	integer i	= -1;
	return maplist (map conf, (list<map>) configurations, {
	    i		= i + 1;
	    return `item (`id (i), conf["name"]:"",conf["name"]:"" == selected);
	});
    }

    // update the combo box with LDAP connections list
    void update_connection_items (string selected) {
	UI::ChangeWidget (`id (`delete), `Enabled, selected != default_name);
	UI::ReplaceWidget (`id (`rpcombo),
	        `ComboBox (`id(`configs), `opt (`hstretch, `notify),
		    // combo box label
		    _("LDAP Connections"), connection_items (selected)
		)
	);
	foreach (string s, [ "server", "bind_dn", "ldap_tls" ], {
	    UI::ChangeWidget (`id (s), `Enabled, selected != default_name);
	    UI::ChangeWidget (`id (s), `Value, s == "ldap_tls" ?
		configuration[s]:false : configuration[s]:"");
	});
    }
    // ask which LDAP connection to choose
    UI::OpenDialog (`opt(`decorated), `HBox (`HSpacing (0.2), `VBox (
	`VSpacing (0.2),
        `HSpacing(40),
	`HBox (
	    `ReplacePoint (`id (`rpcombo),
	        `ComboBox (`id(`configs), `opt (`hstretch, `notify),
		    // combo box label
		    _("LDAP Connections"), []
		)
	    ),
	    `VBox (
		`Label (""),
		`PushButton (`id (`add), Label::AddButton ())
	    ),
	    `VBox (
		`Label (""),
		`PushButton (`id (`delete), Label::DeleteButton ())
	    )
	),
	// textentry label
	`InputField (`id ("server"), `opt (`hstretch, `notify),_("LDAP Server"),
	     configuration["server"]:""),
	`InputField (`id ("bind_dn"),  `opt (`hstretch, `notify),
	    // textentry label
	     _("Administrator DN"), configuration["bind_dn"]:""),
        // password entering label
        `Password (`id("pw"), `opt (`hstretch), _("&LDAP Server Password")),
	`VSpacing (0.2),
	// check box label
	`Left (`CheckBox (`id ("ldap_tls"), `opt (`notify), _("L&DAP TLS"),
	    configuration["ldap_tls"]:false)
	),
        `HBox(
            `PushButton (`id(`ok),`opt(`key_F10, `default), Label::OKButton()),
            // button label
	    `PushButton (`id(`anon), `opt(`key_F6), _("A&nonymous Access")),
            `PushButton (`id(`cancel),`opt(`key_F9), Label::CancelButton())
	),
	`VSpacing (0.2)
    ), `HSpacing (0.2)));

    string current_name	= configuration["name"]:"";
    update_connection_items (current_name);
    any ret = nil;
    while (true)
    {
	ret	= UI::UserInput();
	integer conf= (integer)UI::QueryWidget (`id (`configs), `Value);

	// save configuration currently selected before switching to new one
	if (ret == `ok || ret == `anon || is (ret, string))
	{
	    configuration	= configurations[conf]:$[];
	    current_name	= configuration["name"]:"";
	    if (current_name != default_name)
	    {
		foreach (string s, [ "server", "bind_dn", "ldap_tls" ], {
		    configuration[s]	= UI::QueryWidget (`id (s), `Value);
		});
		integer i	= -1;
		configurations	= maplist (map c, (list<map>) configurations, {
		    i		= i + 1;
		    return i == conf ? configuration : c;
		});
	    }
	}
	if (ret == `configs)
	{
	    configuration	= configurations[conf]:$[];
	    current_name	= configuration["name"]:"";
	    UI::ChangeWidget (`id (`delete), `Enabled,
		current_name != default_name);
	    foreach (string s, [ "server", "bind_dn", "ldap_tls" ], {
		UI::ChangeWidget (`id (s), `Enabled,current_name!=default_name);
		UI::ChangeWidget (`id (s), `Value, s == "ldap_tls" ?
		    configuration[s]:false : configuration[s]:"");
	    });
	}
	if (ret == `add)
	{
	    UI::OpenDialog ( `opt(`decorated), `HBox (`HSpacing (0.2), `VBox (
		`VSpacing (0.2),
		`InputField (`id (`new),
		    // InputField label
		    _("Enter the name of the new LDAP connection")),
		`HBox (
		    `PushButton (`id(`ok), `opt(`default), Label::OKButton ()),
		    `PushButton (`id(`cancel), Label::CancelButton ())
		)
	    ), `HSpacing(0.2)));
	    any r	= UI::UserInput ();
	    string new	= (string) UI::QueryWidget (`id (`new), `Value);
	    UI::CloseDialog ();
	    if (r == `cancel || new == "")
		continue;
	    configuration	= $[
		"name"		: new
	    ];
	    configurations	= add (configurations, configuration);
	    update_connection_items (new);
	}
	if (ret == `delete)
	{
	    configurations	= remove (configurations, conf);
	    update_connection_items (default_name);
	}
	if (ret == `ok || ret == `anon)
	{
	    Ldap::server = (string) UI::QueryWidget (`id ("server"), `Value);
	    Ldap::bind_dn = (string) UI::QueryWidget (`id ("bind_dn"), `Value);
	    Ldap::bind_pass	= (string) UI::QueryWidget(`id("pw"), `Value);
	    Ldap::ldap_tls = (boolean) UI::QueryWidget (`id("ldap_tls"),`Value);
	    Ldap::SetAnonymous (ret == `anon);

	    string error	= Ldap::LDAPInitWithTLSCheck ($[]);
	    if (error != "")
	    {
		Ldap::LDAPErrorMessage ("init", error);
		continue;
	    }

	    error		= Ldap::LDAPBind (Ldap::bind_pass);
	    if (error != "")
	    {
		Ldap::LDAPErrorMessage ("bind", error);
		continue;
	    }
	    error		= Ldap::InitSchema ();
	    if (error != "")
	    {
		Ldap::LDAPErrorMessage ("schema", error);
		continue;
	    }
	    break;
	}
	if (ret == `cancel)
	    break;
    }
    UI::CloseDialog();
    if (ret == `cancel)
    {
	Wizard::CloseDialog ();
	return ret;
    }
    SCR::Write (.target.ycp, configurations_file, configurations);

    // LDAP initialized, we can open the browser now

    set_tree_term ();

    current_dn = (string) UI::QueryWidget (`id(`tree), `CurrentItem);
    if (current_dn == nil) current_dn	= "";

    set_entry_term ();
    if (textmode)
        UI::SetFocus (`id(`tree));

    symbol result	= `notnext;
    symbol current	= `ldaptree;

    while (true) {

	map event       = UI::WaitForEvent ();
	result		= (symbol) event["ID"]:nil;

	if (result == `cancel && !Popup::ReallyAbort (false))
	    result = `not_next;

	if (result == `back || result == `cancel)
	    break;

	if (result == `open)
	    result	= `tree;

	current_dn = (string) UI::QueryWidget (`id(`tree),`CurrentItem);
	if (current_dn == nil)
	    current_dn	= "";

	// switch to different entry while current was modified
	if (result == `tree && Modified ())
	{
	    if (Popup::ContinueCancel (unsaved))
	    {
		// discard the changes
		tmp_data	= $[];
	    }
	    else
	    {
		result	= `not_next;
		continue;
	    }
	}

	// events in tree
	if (result == `tree)
	{
	    if (! dns[current_dn]:false)
	    {
		UI::BusyCursor ();
		subdns = (list<string>) SCR::Read (.ldap.search, $[
		    "base_dn"		: current_dn,
		    "scope"		: 1,
		    "dn_only"		: true,
		    "not_found_ok"	: true,
		]);
		if (subdns == nil)
		{
		    y2warning ("the search for %1 returned nil...", current_dn);
		    continue;
		}
		else subdns	= sort (subdns);
		dns [current_dn]	= true;
		if (size (subdns) > 0)
		// TODO if size (subdns) > 0) || dn has glyph
		{
		    open_items = (map) UI::QueryWidget (`tree, `OpenItems);
		    tree_items	= update_items (tree_items);
		    UI::ReplaceWidget (`id (`reptree), textmode ?
			`Tree (`id(`tree), root_dn, tree_items) :
			`Tree (`id(`tree), `opt(`notify), root_dn, tree_items));
		    UI::ChangeWidget (`id(`tree), `CurrentItem, current_dn);
		    open_items = $[];
		}
		current_dn = (string) UI::QueryWidget (`id(`tree),`CurrentItem);
		if (current_dn == nil) current_dn	= "";
	    }
	    data		= Ldap::GetLDAPEntry (current_dn);
	    tmp_data		= $[];
	    set_entry_term ();
	    UI::NormalCursor ();
	    if (textmode)
		UI::SetFocus (`id(`tree));
	}

	if (result == `reload)
	{
	    tree_items	= [];
	    open_items	= $[];
	    dns		= $[];
	    topdns	= $[];
	    subdns	= [];
	    root_dn	= "";
	    set_tree_term ();
	}
	// events in Edit Entry part
	if (result == `edit)
	    result	= `table;
	if (result == `table && event["EventReason"]:"" == "SelectionChanged")
	{
	    string attr = (string) UI::QueryWidget (`id(`table), `CurrentItem);
	    boolean enable	= true;
	    if (size (attr) < size (current_dn) &&
		substring (current_dn, 0, size (attr) + 1) == (attr + "="))
	    {
		y2debug ("disabling %1 for editing...", attr);
		enable		= false;
	    }
	    if (attr == "objectClass")
		enable		= false;
	    UI::ChangeWidget (`id (`edit), `Enabled, enable);
	}
	else if (result == `table)
	{
	    string attr	= (string) UI::QueryWidget (`id(`table), `CurrentItem);
	    if (UI::QueryWidget  (`id (`edit), `Enabled) == false)
	    {
		y2milestone ("editing the value of attribute '%1' is not allowed", attr);
		result = `notnext;
		continue;
	    }
	    list<string> value	= tmp_data [attr]:data[attr]:[];
	    value		= LdapPopup::EditAttribute ($[
		"attr"          : attr,
		"value"         : value,
		"single"        : Ldap::SingleValued (attr)
	    ]);
	    if (value == tmp_data [attr]:data[attr]:[])
	    {
		result = `notnext;
		continue;
	    }
	    UI::ChangeWidget(`id(`table),`Item(attr,1), mergestring(value,","));
	    UI::ChangeWidget (`id(`save), `Enabled, true);
	    tmp_data [attr] = value;
	}
	if (result == `save)
	{
	    if (Modified ())
	    {
		boolean cont	= false;
		foreach (string oc, data["objectClass"]:[], {
		    if (cont) return;
		    foreach (string attr, Ldap::GetRequiredAttributes (oc), {
			any val = tmp_data[attr]:nil;
			if (!cont && (val == [] || val == "")) {
			    //error popup, %1 is attribute name
			    Popup::Error (sformat (_("The \"%1\" attribute is mandatory.
Enter a value."), attr));
			    UI::SetFocus (`id(`table));
			    cont = true;
			}
		    });
		});
		if (cont)
		{
		    result	= `not_next;
		    continue;
		}
		if (tmp_data["modified"]:"" == "")
		    tmp_data["modified"]	= "edited";
		if (Ldap::WriteLDAP ($[ current_dn : tmp_data ]))
		{
		    tmp_data		= $[];
		    UI::ChangeWidget (`id(`save), `Enabled, false);
		}
	    }
	}

	// general events
	if (result == `next)
	{
	    if (Modified () && !Popup::ContinueCancel (unsaved))
	    {
		result	= `not_next;
		continue;
	    }
	    break;
	}
    }
    Wizard::CloseDialog ();
    return `finish;
}

ACC SHELL 2018