* * @return boolean true if Ppolicy is totally supported, false in other case * * @author Benjamin Renard */ function LSaddon_ppolicy_support() { $retval = true; $MUST_DEFINE_CONST = array( 'LS_PPOLICY_DEFAULT_DN', 'LS_PPOLICY_WARNING_EXPIRATION_THRESHOLD', 'LS_PPOLICY_CRITICAL_EXPIRATION_THRESHOLD', 'LS_PPOLICY_CSV_DELIMITER', 'LS_PPOLICY_CSV_ENCLOSURE', 'LS_PPOLICY_CSV_ESCAPE_CHAR', ); foreach($MUST_DEFINE_CONST as $const) { if (!defined($const)) { LSerror :: addErrorCode('PPOLICY_SUPPORT_01', $const); $retval = false; } } $MUST_DEFINE_ARRAY= array( 'LS_PPOLICY_API_GRANTED_PROFILES', 'LS_PPOLICY_INFO_EXPORT_EXTRA_ATTRS', ); foreach($MUST_DEFINE_ARRAY as $array) { if ( !isset($GLOBALS[$array]) || !is_array($GLOBALS[$array])) { LSerror :: addErrorCode('PPOLICY_SUPPORT_02', $array); $retval = false; } } // Init ppolicy objects cache $GLOBALS['PPOLICY_OBJECTS_CACHE'] = array(); if ($retval) { LSurl :: add_handler( '#^api/1.0/exportPpolicyInfo/(?P[^/]+)/?$#', 'handle/export_api_LSobject_exportPpolicyInfo', true, false, true); } return $retval; } /** * Retrieve ppolicy object (from cache if already loaded) * * @param[in] $dn string The DN of the ppolicy object * * @return array|false Array of ppolicy object's attributes on success, false otherwise * * @author Benjamin Renard */ function get_ppolicy_object($dn) { if (array_key_exists($dn, $GLOBALS['PPOLICY_OBJECTS_CACHE'])) return $GLOBALS['PPOLICY_OBJECTS_CACHE'][$dn]; $GLOBALS['PPOLICY_OBJECTS_CACHE'][$dn] = LSldap :: getAttrs($dn, '(objectClass=pwdPolicy)'); return $GLOBALS['PPOLICY_OBJECTS_CACHE'][$dn]; } /** * Retrieve ppolicy password max age * * @param[in] $ppolicy_dn string Optional DN of the ppolicy object to use * * @return int|null|false The ppolicy password max age (in second) if defined, null if no password max age is defined or false in case of error * * @author Benjamin Renard */ function get_ppolicy_password_max_age($ppolicy_dn=null) { if (!$ppolicy_dn) $ppolicy_dn = LS_PPOLICY_DEFAULT_DN; if (!$ppolicy_dn) return null; $ppolicy = get_ppolicy_object($ppolicy_dn); if (!$ppolicy) return false; if (isset($ppolicy['pwdMaxAge'])) return intval($ppolicy['pwdMaxAge']); return null; } /** * Format and return HTML code of a badge * * @param[in] $text string The text of the badge * @param[in] $bg_color string The background color of the badge (optional, default: green) * @param[in] $color string The text color of the badge (optional, default: white) * * @return string The HTML code of the badge * * @author Benjamin Renard */ function _ppolicy_badge($text, $bg_color='green', $color='white') { // Disable HTML formating on PHP cli if (php_sapi_name() == 'cli') return $text; return sprintf( '%s', $bg_color, $color, $text ); } /** * Retrieve Ppolicy extraDisplayedColumn password expiration * * @param[in] $entry An LSsearchEntry object * * @return string Ppolicy extraDisplayedColumn password expiration * * @author Benjamin Renard */ function ppolicy_extraDisplayColumn_password_expiration($entry) { $change_time = $entry->pwdChangedTime; if (!$change_time) return _('Never'); $change_time = ldapDate2Timestamp($change_time); $max_age = get_ppolicy_password_max_age($entry->pwdPolicySubentry); if ($max_age === false) return _ppolicy_badge(__('Unknown'), 'gray'); if (!$max_age) return _('Never'); $expiration_date = $change_time + $max_age; $now = time(); if ($expiration_date <= $now) return _ppolicy_badge( sprintf(_('Expired (since %s)'), date('Y-m-d H:i', $expiration_date)), 'black'); $delta = $expiration_date - $now; if ($delta <= LS_PPOLICY_CRITICAL_EXPIRATION_THRESHOLD) $badge_color = 'red'; elseif ($delta <= LS_PPOLICY_WARNING_EXPIRATION_THRESHOLD) $badge_color = 'orange'; else $badge_color = 'green'; return _ppolicy_badge( sprintf(_('Expire on %s'), date('Y-m-d H:i', $expiration_date)), $badge_color); } /** * Write LSsearch result as CSV and force download of it. * * @param[in] $LSsearch The LSsearch object * * @author Benjamin Renard * * @return boolean Void if CSV file is successfully generated and upload, false in other case */ function ppolicy_export_search_info($LSsearch, $as_csv=true, $return=false) { if ($as_csv) { $csv = fopen('php://temp/maxmemory:'. (5*1024*1024), 'r+'); if ($csv === false) { LSerror :: addErrorCode('PPOLICY_01'); return false; } } $attrs = array_merge( array( 'pwdPolicySubentry', 'pwdChangedTime', 'pwdGraceUseTime', 'pwdFailureTime', 'pwdAccountLockedTime', 'pwdReset', 'pwdHistory' ), $GLOBALS['LS_PPOLICY_INFO_EXPORT_EXTRA_ATTRS'] ); $LSsearch -> setParam('attributes', $attrs); if (!$LSsearch -> run()) { LSerror :: addErrorCode('PPOLICY_02'); return false; } if ($as_csv) { $headers = array($LSsearch->label_objectName, 'DN'); foreach($attrs as $attr) { $label = LSconfig::get("LSobjects.".$LSsearch->LSobject.".attrs.$attr.label", $attr, 'string'); $headers[] = __($label); } if (!_ppolicy_write_row_in_csv($csv, $headers)) { LSerror :: addErrorCode('PPOLICY_03'); return false; } } else { $data = array(); } foreach ($LSsearch -> getSearchEntries() as $e) { $row = array( 'name' => $e -> displayName, 'dn' => $e -> dn, ); foreach($attrs as $attr) { if ($as_csv) { $values = ensureIsArray($e -> get($attr)); if ($values) { $row[] = ($as_json?$values:implode('|', $values)); } else { $no_value_label = LSconfig::get( "LSobjects.".$LSsearch->LSobject.".attrs.$attr.no_value_label", ___('Not set'), 'string'); $row[] = __($no_value_label); } } else { $row[$attr] = ensureIsArray($e -> get($attr)); if (!LSconfig::get("LSobjects.".$LSsearch->LSobject.".attrs.$attr.multiple")) { $row[$attr] = ($row[$attr]?$row[$attr][0]:null); } } } if ($as_csv) { if (!_ppolicy_write_row_in_csv($csv, $row)) { LSerror :: addErrorCode('PPOLICY_04'); return false; } } else { $data[] = $row; } } if (!$as_csv) { if ($return) return $data; header("Content-disposition: attachment; filename=ppolicy-".$LSsearch->LSobject.".json"); LSsession :: displayAjaxReturn($data); exit(); } rewind($csv); if ($return) { $data = stream_get_contents($csv); @fclose($csv); return $data; } header("Content-disposition: attachment; filename=ppolicy-".$LSsearch->LSobject.".csv"); header("Content-type: text/csv"); print stream_get_contents($csv); @fclose($csv); exit(); } /** * Write CSV row in file * * @param[in] $csv The CSV file description reference * @param[in] $row An array of a CSV row fields to write * * @author Benjamin Renard * * @retval boolean True if CSV row is successfully writed, false in other case */ function _ppolicy_write_row_in_csv(&$csv, &$row) { if (!defined('PHP_VERSION_ID') or PHP_VERSION_ID < 50504) { return ( fputcsv($csv, $row, LS_PPOLICY_CSV_DELIMITER, LS_PPOLICY_CSV_ENCLOSURE) !== false ); } return ( fputcsv( $csv, $row, LS_PPOLICY_CSV_DELIMITER, LS_PPOLICY_CSV_ENCLOSURE, LS_PPOLICY_CSV_ESCAPE_CHAR) !== false ); } /** * Handle exportPpolicyInfo API request * * @param[in] $request LSurlRequest The request * * @return void **/ function handle_api_LSobject_exportPpolicyInfo($request) { $object = get_LSobject_from_API_request($request); if (!$object) return; $container_dn = LSconfig::get( "LSobjects.".$object->LSobject.".container_dn", "", "string"); $whoami = LSsession :: whoami( $container_dn? $container_dn.','.LSsession::getTopDn(): LSsession::getTopDn() ); if (!array_intersect($GLOBALS['LS_PPOLICY_API_GRANTED_PROFILES'], $whoami)) { LSerror :: addErrorCode('LSsession_11'); LSsession :: displayAjaxReturn(); return false; } if (!LSsession :: loadLSclass('LSsearch')) { LSerror :: addErrorCode('LSsession_05', 'LSsearch'); LSsession :: displayAjaxReturn(); return false; } $search = new LSsearch( $request->LSobject, 'api' ); $search -> setParam('onlyAccessible', True); $data = ppolicy_export_search_info($search, false, true); LSsession :: displayAjaxReturn($data); } if (php_sapi_name() != 'cli') return true; function cli_export_ppolicy_info($command_args) { $objType = null; $output = false; $json = false; $pretty = false; for ($i=0; $i < count($command_args); $i++) { switch($command_args[$i]) { case '-o': case '--output': $i++; $output = $command_args[$i]; break; case '-j': case '--json': $json = true; break; case '-p': case '--pretty': $pretty = true; break; default: if (is_null($objType)) $objType = $command_args[$i]; else LScli :: usage("Invalid ".$command_args[$i]." parameter."); break; } } if (is_null($objType)) LScli :: usage('You must provide LSobject type.'); if (!LSsession :: loadLSobject($objType)) return false; if (!LSsession :: loadLSclass('LSsearch')) { LSerror :: addErrorCode('LSsession_05', 'LSsearch'); LSsession :: displayAjaxReturn(); return false; } $search = new LSsearch($objType, 'cli_export_ppolicy_info'); $search -> setParam('onlyAccessible', True); $data = ppolicy_export_search_info($search, !$json, true); if ($json) $data = json_encode( $data, ($pretty?JSON_PRETTY_PRINT:0) ); if (!$output) { print($data); exit(); } $fd = fopen($output, 'w') or LStemplate::fatal_error("Fail to open output file '$output'"); fwrite($fd, $data) or LStemplate::fatal_error("Fail to write result in output file '$output'"); @fclose($fd); } /** * Args autocompleter for CLI export_ppolicy_info command * * @param[in] $command_args array List of already typed words of the command * @param[in] $comp_word_num int The command word number to autocomplete * @param[in] $comp_word string The command word to autocomplete * @param[in] $opts array List of global available options * * @retval array List of available options for the word to autocomplete **/ function cli_export_ppolicy_info_args_autocompleter($command_args, $comp_word_num, $comp_word, $opts) { $opts = array_merge($opts, array ('-o', '--output', '-j', '--json', '-p', '--pretty')); // Handle positional args $objType = null; $objType_arg_num = null; for ($i=0; $i < count($command_args); $i++) { if (!in_array($command_args[$i], $opts)) { // If object type not defined if (is_null($objType)) { // Defined it $objType = $command_args[$i]; LScli :: unquote_word($objType); $objType_arg_num = $i; // Check object type exists $objTypes = LScli :: autocomplete_LSobject_types($objType); // Load it if exist and not trying to complete it if (in_array($objType, $objTypes) && $i != $comp_word_num) { LSsession :: loadLSobject($objType, false); } } } } // If objType not already choiced (or currently autocomplete), add LSobject types to available options if (!$objType || $objType_arg_num == $comp_word_num) $opts = array_merge($opts, LScli :: autocomplete_LSobject_types($comp_word)); return LScli :: autocomplete_opts($opts, $comp_word); } LScli :: add_command( 'export_ppolicy_info', 'cli_export_ppolicy_info', 'Export password policy info of a all objects of a specified object type', '[object type] [-o|--output filepath] [-j|--json [-p|--pretty]]', false, // long desc true, 'cli_export_ppolicy_info_args_autocompleter' );