diff --git a/install/db/dedalo4_install.backup b/install/db/dedalo4_install.backup index 2f2dc8e27e..86c30b4c62 100644 Binary files a/install/db/dedalo4_install.backup and b/install/db/dedalo4_install.backup differ diff --git a/lib/dedalo/component_autocomplete/component_autocomplete.php b/lib/dedalo/component_autocomplete/component_autocomplete.php index 837344bc3c..824b4d2709 100644 --- a/lib/dedalo/component_autocomplete/component_autocomplete.php +++ b/lib/dedalo/component_autocomplete/component_autocomplete.php @@ -1,11 +1,11 @@ get_tipo(); $parent = $this->get_parent(); $section_tipo = $this->get_section_tipo(); - $modo = $this->get_modo(); + $modo = $this->get_modo(); $label = $this->get_label(); $required = $this->get_required(); $propiedades = $this->get_propiedades(); @@ -21,14 +21,14 @@ $component_name = get_class($this); if($permissions===0) return null; - + # Verify component content record is inside section record filter if ($this->get_filter_authorized_record()===false) return NULL; $file_name = $modo; $from_modo = $modo; - - switch($modo) { + + switch($modo) { case 'edit_in_list': // Fix always edit as modo / filename @@ -40,48 +40,48 @@ } $wrap_style = ''; - // Dont break here. Continue as modo edit + // Dont break here. Continue as modo edit case 'tool_description': // Take care previous case don't break ! if ($modo==='tool_description') { $file_name = 'edit'; - } + } // Dont break here. Continue as modo edit - case 'edit' : + case 'edit' : - # Custom propiedades external dato + # Custom propiedades external dato if(isset($propiedades->source->mode) && $propiedades->source->mode==='external'){ $this->set_dato_external($save=false, $changed=false, $current_dato=false); // Forces update dato with calculated external dato $dato = $this->get_dato(); } - - // General vars + + // General vars $tipo_to_search = $this->get_tipo_to_search(); $ar_valor = $this->get_valor($lang,'array'); $ar_labels = array_map(function($element){ return $element->label; - }, $ar_valor); + }, $ar_valor); $valor = implode('
', $ar_labels); - + $id_wrapper = 'wrapper_'.$identificador_unico; $input_name = "{$tipo}_{$parent}"; $component_info = $this->get_component_info('json'); - $dato_json = json_handler::encode($dato); + $dato_json = json_handler::encode($dato); // service autocomplete options $ar_target_section_tipo = $this->get_ar_target_section_tipo(); // search_sections . set and remove search sections duplicates $search_sections = array_values( array_unique($ar_target_section_tipo) ); - + // from_modo. get the change modo from portal list to edit $from_modo_requested = common::get_request_var('from_modo'); if ($from_modo_requested!==false) { $from_modo = $from_modo_requested; } - + // Semantic nodes $semantic_nodes = $this->get_semantic_nodes(); if ( !empty($this->semantic_nodes) ) { @@ -93,7 +93,7 @@ // time machine. get the change in_time_machine $var_requested_m = common::get_request_var('m'); $var_requested = common::get_request_var('mode'); - $in_time_machine = (!empty($var_requested_m) && $var_requested_m==='tool_time_machine') || + $in_time_machine = (!empty($var_requested_m) && $var_requested_m==='tool_time_machine') || (!empty($var_requested) && $var_requested==='load_preview_component') ? true : false; // Filter_by_list (Propiedades option) @@ -102,14 +102,14 @@ $filter_by_list = $propiedades->source->filter_by_list; } $json_filter_by_list = json_encode($filter_by_list); - + // search_query_object params # Limit - $limit = isset($propiedades->limit) ? (int)$propiedades->limit : 0; + $limit = isset($propiedades->limit) ? (int)$propiedades->limit : 0; # Divisor $divisor = $this->get_divisor(); # q_operator is injected by trigger search2 - $q_operator = isset($this->q_operator) ? $this->q_operator : null; + $q_operator = isset($this->q_operator) ? $this->q_operator : null; // search_query_object build $query_object_options = new stdClass(); @@ -124,7 +124,7 @@ $json_search_query_object = json_encode( $search_query_object, JSON_UNESCAPED_UNICODE | JSON_HEX_APOS ); #$filter_by_list_component_tipo = isset($propiedades->source->filter_by_list->component_tipo) ? $propiedades->source->filter_by_list->component_tipo : null; - + // Dataframe manager $ar_dataframe_obj = array(); $ar_dataframe = isset($propiedades->dataframe) ? $propiedades->dataframe : false; @@ -135,12 +135,12 @@ $dataframe_obj = new dataframe($current_dataframe->tipo, $current_dataframe->type, $this, 'dataframe_edit', $key); $ar_dataframe_obj[] = $dataframe_obj; //dump($ar_dataframe_obj[0]->get_html(), ' $ar_dataframe_obj[$i]->get_html(); ++ '.to_string()); - } + } } } } break; - + case 'search': // General vars # dato is injected by trigger search wen is needed @@ -150,29 +150,29 @@ $ar_valor = $this->get_valor($lang,'array'); $ar_labels = array_map(function($element){ return $element->label; - }, $ar_valor); + }, $ar_valor); $valor = implode('
', $ar_labels); $id_wrapper = 'wrapper_'.$identificador_unico; - $input_name = $tipo.'_'.$parent; - $tipo_to_search = $this->get_tipo_to_search(); + $input_name = $tipo.'_'.$parent; + $tipo_to_search = $this->get_tipo_to_search(); # Search input name (var search_input_name is injected in search -> records_search_list.phtml) # and recovered in component_common->get_search_input_name() # Normally is section_tipo + component_tipo, but when in portal can be portal_tipo + section_tipo + component_tipo $search_input_name = $this->get_search_input_name(); $component_info = $this->get_component_info('json'); - $in_time_machine = false; + $in_time_machine = false; // Filter_by_list (Propiedades option) $filter_by_list = false; // Default if (isset($propiedades->source->filter_by_list)) { $filter_by_list = $propiedades->source->filter_by_list; } - $json_filter_by_list = json_encode($filter_by_list); + $json_filter_by_list = json_encode($filter_by_list); // service autocomplete options $ar_target_section_tipo = $this->get_ar_target_section_tipo(); - $search_sections = $ar_target_section_tipo; - + $search_sections = $ar_target_section_tipo; + // Filter_by_list (Propiedades option) $filter_by_list = false; // Default if (isset($propiedades->source->filter_by_list)) { @@ -184,7 +184,7 @@ $limit = 1; # q_operator is injected by trigger search2 $q_operator = isset($this->q_operator) ? $this->q_operator : null; - $divisor = $this->get_divisor(); + $divisor = $this->get_divisor(); // search_query_object build $query_object_options = new stdClass(); @@ -198,7 +198,7 @@ //search mode always can edit the field, permissions always in 2 $permissions = 2; - + $file_name = 'edit'; break; @@ -218,19 +218,19 @@ case 'portal_list': $id_wrapper = 'wrapper_'.$identificador_unico; - $tipo_to_search = $this->get_tipo_to_search(); + $tipo_to_search = $this->get_tipo_to_search(); $ar_target_section_tipo = $this->get_ar_target_section_tipo(); $ar_target_section_tipo_json = json_encode($ar_target_section_tipo); $dato_json = json_encode($dato); $component_info = $this->get_component_info('json'); $valor = $this->get_valor($lang,'string'); $ar_valor = $this->get_valor($lang,'array'); - + $file_name = 'list'; break; - + case 'list': - # Return direct value for store in 'valor_list' + # Return direct value for store in 'valor_list' $valor = $this->get_valor($lang,'string'); echo (string)$valor; # Like "Catarroja, L'Horta Sud, Valencia/València, Comunidad Valenciana, España" return; // NOT load html file @@ -253,8 +253,8 @@ break; } - - + + $page_html = DEDALO_LIB_BASE_PATH .'/'. get_class($this) . '/html/' . get_class($this) . '_' . $file_name . '.phtml'; if( !include($page_html) ) { echo "
Invalid mode $this->modo
"; diff --git a/lib/dedalo/component_common/class.component_common.php b/lib/dedalo/component_common/class.component_common.php index b9965dda77..bdaebf66e8 100755 --- a/lib/dedalo/component_common/class.component_common.php +++ b/lib/dedalo/component_common/class.component_common.php @@ -2817,7 +2817,14 @@ public function get_ar_target_section_tipo() { if(isset($propiedades->source->search)){ foreach ($propiedades->source->search as $current_search) { - $ar_terminoID_by_modelo_name[] = $current_search->section_tipo; + $current_section_tipo = $current_search->section_tipo; + // check if is available + $current_model = RecordObj_dd::get_modelo_name_by_tipo($current_section_tipo,true); + if ($current_model==='section') { + $ar_terminoID_by_modelo_name[] = $current_section_tipo; + }else{ + debug_log(__METHOD__." !!!!!!!!!!! IGNORED NO SECTION element: $current_section_tipo ".to_string($current_model), logger::ERROR); + } } } @@ -4341,8 +4348,8 @@ public function autocomplete_search($search_query_object, $divisor=', ') { // debug if(SHOW_DEBUG===true) { - debug_log(__METHOD__." search_query_object - modo:$this->modo - ".json_encode($search_query_object, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), logger::DEBUG); - debug_log(__METHOD__." rows_data->strQuery ".to_string($rows_data->strQuery), logger::DEBUG); + // debug_log(__METHOD__." search_query_object - modo:$this->modo - ".json_encode($search_query_object, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), logger::DEBUG); + // debug_log(__METHOD__." rows_data->strQuery ".to_string($rows_data->strQuery), logger::DEBUG); } // childrens addition optional diff --git a/lib/dedalo/component_image/component_image_json.php b/lib/dedalo/component_image/component_image_json.php index 41cb01c857..b4d1c521c5 100644 --- a/lib/dedalo/component_image/component_image_json.php +++ b/lib/dedalo/component_image/component_image_json.php @@ -12,7 +12,7 @@ // Available image files $value = []; - + //$ar_quality = $this->get_ar_image_quality(); //foreach ($ar_quality as $quality) { // $image_url = $this->get_image_url($quality, true, true, false); // ($quality=false, $test_file=true, $absolute=false, $default_add=true) @@ -42,4 +42,4 @@ $data[] = $item; // JSON string - return common::build_element_json_output($context, $data); \ No newline at end of file + return common::build_element_json_output($context, $data); diff --git a/lib/dedalo/config/version.inc b/lib/dedalo/config/version.inc index a980dfa68e..e0c0d4fd93 100644 --- a/lib/dedalo/config/version.inc +++ b/lib/dedalo/config/version.inc @@ -1653,12 +1653,12 @@ # Version - $DEDALO_VERSION = '5.5.9'; + $DEDALO_VERSION = '5.5.9b'; if(defined('DEVELOPMENT_SERVER') && DEVELOPMENT_SERVER===true) { $DEDALO_VERSION .= '.'.time(); } define('DEDALO_VERSION' , $DEDALO_VERSION); - define('DEDALO_BUILD' , '02-01-2020'); + define('DEDALO_BUILD' , '08-01-2020'); /* diff --git a/lib/dedalo/db/class.data_check.php b/lib/dedalo/db/class.data_check.php index 70a62040ed..a9e09bff93 100644 --- a/lib/dedalo/db/class.data_check.php +++ b/lib/dedalo/db/class.data_check.php @@ -12,96 +12,104 @@ class data_check { * CHECK_SEQUENCES * @return stdClass object $response */ - public function check_sequences() { - + public function check_sequences() { + $response = new stdClass(); $response->result = true; $response->msg = ''; - // SHOW server_version; - $sql = " SHOW server_version; "; - $result_v = JSON_RecordObj_matrix::search_free($sql); - $server_version = pg_fetch_result($result_v, 0, 'server_version'); - $ar_parts = explode('.', $server_version); - $server_major_version = (int)$ar_parts[0]; - #dump($server_major_version, ' server_version ++ '.to_string()); - - $response->msg .= "TEST ALL SEQUENCES IN DATABASE: ".DEDALO_DATABASE_CONN; - - $ar_skip_tables = array(); // 'sqlmapfile','sqlmapoutput' - - - # Find and iterate all db tables - $sql = " SELECT table_name FROM information_schema.tables WHERE table_schema='public' ORDER BY table_name ASC "; - $result = JSON_RecordObj_matrix::search_free($sql); - while ($rows = pg_fetch_assoc($result)) { - - $table_name = $rows['table_name']; - - if (in_array($table_name, $ar_skip_tables)) { - continue; // Skip table - } - - // Detected sqlmap tables. 'sqlmapfile','sqlmapoutput' - if (strpos($table_name, 'sqlmap')!==false) { - throw new Exception("Error Processing Request. Security sql injection warning", 1); - } - - # Find last id in table - $sql = " SELECT id FROM $table_name ORDER BY id DESC LIMIT 1 "; - $result2 = JSON_RecordObj_matrix::search_free($sql); - if (pg_num_rows($result2) === 0) { - continue; // Skip empty tables - } - $last_id = pg_fetch_result($result2, 0, 'id'); - - # Find vars in current sequence - if ($server_major_version>=10) { - $search_table = 'sequencename'; - $sql = " SELECT last_value, start_value FROM pg_sequences WHERE $search_table = '".$table_name."_id_seq' ; "; - }else{ - $search_table = $table_name."_id_seq"; - $sql = " SELECT last_value, start_value FROM $search_table ; "; - } - $result_seq = JSON_RecordObj_matrix::search_free($sql); - if (pg_num_rows($result_seq) === 0) { - debug_log(__METHOD__." Warning. {$table_name}_id_seq not found in $search_table ".to_string(), logger::WARNING); - continue; // Skip empty tables - } - $last_value = pg_fetch_result($result_seq, 0, 'last_value'); - $start_value = pg_fetch_result($result_seq, 0, 'start_value'); - - $response->msg .= "
$table_name - start_value: $start_value - seq last_value: $last_value "; - if ($last_value!=$last_id) { - #$response->msg .= "[last id: $last_id] ALTER SEQUENCE {$table_name}_id_seq RESTART WITH $last_id;"; - $response->msg .= "[last id: $last_id] SELECT setval('public.{$table_name}_id_seq', $last_id, true);"; - }else{ - $response->msg .= "[last id: $last_id]"; - } - - - if ($last_id>$last_value) { - $response->msg .= "
WARNING: seq last_id > last_value [$last_id > $last_value]"; - $response->msg .= "
FIX AUTOMATIC TO $last_id start"; - #$response->msg .= "Use:
SELECT setval('public.{$table_name}_id_seq', $last_id, true);
"; - - $sql2 = "SELECT setval('public.{$table_name}_id_seq', $last_id, true);"; - $result2 = JSON_RecordObj_matrix::search_free($sql2); - if (!$result2) { - $response->msg .= "Use: SELECT setval('public.{$table_name}_id_seq', $last_id, true);"; + try { + + // SHOW server_version; + $sql = " SHOW server_version; "; + $result_v = JSON_RecordObj_matrix::search_free($sql); + $server_version = pg_fetch_result($result_v, 0, 'server_version'); + $ar_parts = explode('.', $server_version); + $server_major_version = (int)$ar_parts[0]; + #dump($server_major_version, ' server_version ++ '.to_string()); + + $response->msg .= "TEST ALL SEQUENCES IN DATABASE: ".DEDALO_DATABASE_CONN; + + $ar_skip_tables = array(); // 'sqlmapfile','sqlmapoutput' + + + # Find and iterate all db tables + $sql = " SELECT table_name FROM information_schema.tables WHERE table_schema='public' ORDER BY table_name ASC "; + $result = JSON_RecordObj_matrix::search_free($sql); + while ($rows = pg_fetch_assoc($result)) { + + $table_name = $rows['table_name']; + + if (in_array($table_name, $ar_skip_tables)) { + continue; // Skip table } - $response->result = false; - } + // Detected sqlmap tables. 'sqlmapfile','sqlmapoutput' + if (strpos($table_name, 'sqlmap')!==false) { + throw new Exception("Error Processing Request. Security sql injection warning", 1); + } - if ($start_value!=1) { - $response->msg .= "
WARNING: seq start_value != 1"; - $response->msg .= "Use: ALTER SEQUENCE {$table_name}_id_seq START WITH 1 ;"; + # Find last id in table + $sql = " SELECT id FROM $table_name ORDER BY id DESC LIMIT 1 "; + $result2 = JSON_RecordObj_matrix::search_free($sql); + if (pg_num_rows($result2) === 0) { + continue; // Skip empty tables + } + $last_id = pg_fetch_result($result2, 0, 'id'); + + # Find vars in current sequence + if ($server_major_version>=10) { + $search_table = 'sequencename'; + $sql = " SELECT last_value, start_value FROM pg_sequences WHERE $search_table = '".$table_name."_id_seq' ; "; + }else{ + $search_table = $table_name."_id_seq"; + $sql = " SELECT last_value, start_value FROM $search_table ; "; + } + $result_seq = JSON_RecordObj_matrix::search_free($sql); + if (pg_num_rows($result_seq) === 0) { + debug_log(__METHOD__." Warning. {$table_name}_id_seq not found in $search_table ".to_string(), logger::WARNING); + continue; // Skip empty tables + } + $last_value = pg_fetch_result($result_seq, 0, 'last_value'); + $start_value = pg_fetch_result($result_seq, 0, 'start_value'); + + $response->msg .= "
$table_name - start_value: $start_value - seq last_value: $last_value "; + if ($last_value!=$last_id) { + #$response->msg .= "[last id: $last_id] ALTER SEQUENCE {$table_name}_id_seq RESTART WITH $last_id;"; + $response->msg .= "[last id: $last_id] SELECT setval('public.{$table_name}_id_seq', $last_id, true);"; + }else{ + $response->msg .= "[last id: $last_id]"; + } + + + if ($last_id>$last_value) { + $response->msg .= "
WARNING: seq last_id > last_value [$last_id > $last_value]"; + $response->msg .= "
FIX AUTOMATIC TO $last_id start"; + #$response->msg .= "Use:
SELECT setval('public.{$table_name}_id_seq', $last_id, true);
"; + + $sql2 = "SELECT setval('public.{$table_name}_id_seq', $last_id, true);"; + $result2 = JSON_RecordObj_matrix::search_free($sql2); + if (!$result2) { + $response->msg .= "Use: SELECT setval('public.{$table_name}_id_seq', $last_id, true);"; + } + + $response->result = false; + } + + if ($start_value!=1) { + $response->msg .= "
WARNING: seq start_value != 1"; + $response->msg .= "Use: ALTER SEQUENCE {$table_name}_id_seq START WITH 1 ;"; + + $response->result = false; + } - $response->result = false; - } + }//end while ($rows = pg_fetch_assoc($result)) - }//end while ($rows = pg_fetch_assoc($result)) { + } catch (Exception $e) { + $response->result = false; + $response->msg = 'Caught exception: ' . $e->getMessage(); + return $response; + } return (object)$response; @@ -110,4 +118,4 @@ public function check_sequences() { }//end data_check -?> \ No newline at end of file +?> diff --git a/lib/dedalo/diffusion/class.diffusion_rdf.php b/lib/dedalo/diffusion/class.diffusion_rdf.php index 1da2b8f487..d265a87c13 100644 --- a/lib/dedalo/diffusion/class.diffusion_rdf.php +++ b/lib/dedalo/diffusion/class.diffusion_rdf.php @@ -30,7 +30,7 @@ class diffusion_rdf extends diffusion { * @param object $options . Default null */ function __construct($options=null) { - + parent::__construct($options=null); $this->DEDALO_EXTRAS_BASE_URL = DEDALO_ROOT_WEB . '/'. basename(dirname(DEDALO_LIB_BASE_PATH)) .'/'. basename(DEDALO_LIB_BASE_PATH) .'/'. basename(DEDALO_EXTRAS_PATH); @@ -51,22 +51,22 @@ public function update_record( $request_options, $resolve_references=false ) { $options = new stdClass(); $options->section_tipo = null; $options->section_id = null; - $options->diffusion_element_tipo= null; + $options->diffusion_element_tipo= null; foreach ($request_options as $key => $value) {if (property_exists($options, $key)) $options->$key = $value;} - + // target_section_tipo $RecordObj_dd = new RecordObj_dd($options->diffusion_element_tipo); $propiedades = $RecordObj_dd->get_propiedades(true); - #$target_section_tipo = $propiedades->diffusion->target_section_tipo; - + #$target_section_tipo = $propiedades->diffusion->target_section_tipo; + // Fix vars $this->service_name = $propiedades->diffusion->service; $this->entity_section_id = $options->section_id; - + // search records (Fichero) /* $target_rows = self::get_target_rows($target_section_tipo, $options->section_tipo, $options->section_id); - + $ar_section_id = array_map(function($item){ return $item->section_id; }, $target_rows); @@ -84,7 +84,7 @@ public function update_record( $request_options, $resolve_references=false ) { return $item->section_id; }, (array)$this->ar_records); } - + // filter to publish records $ar_section_id = self::get_to_publish_rows($options->section_tipo, $ar_section_id); @@ -98,7 +98,7 @@ public function update_record( $request_options, $resolve_references=false ) { } debug_log(__METHOD__." CREATED DIR: $folder_path ".to_string(), logger::DEBUG); } - + // Filename. Format like: '1.rdf' for multiple or '1_1.rdf' for one record (entity_id.rdf, entity_id_section_id.rdf) $collection_tipo = $propiedades->diffusion->collection_tipo; // expected 'numisdata159' @@ -109,7 +109,7 @@ public function update_record( $request_options, $resolve_references=false ) { 'list', DEDALO_DATA_LANG, $options->section_tipo); // expected 'numisdata4' - $dato = $component->get_dato(); // Get array of locators. One expected + $dato = $component->get_dato(); // Get array of locators. One expected $collection_id = isset($dato[0]->section_id) ? $dato[0]->section_id : null; if (count($ar_section_id)===1) { @@ -117,22 +117,22 @@ public function update_record( $request_options, $resolve_references=false ) { }else{ $rdf_file_name = $collection_id . '.rdf'; } - - - - // diffusion rdf + + + + // diffusion rdf $xml_tipo = RecordObj_dd::get_ar_terminoID_by_modelo_name_and_relation($options->diffusion_element_tipo, 'xml', 'children', true)[0]; $xml_options = new stdClass(); $xml_options->xml_tipo = $xml_tipo; // Numisma RDF : modelo_name : xml $xml_options->section_tipo = $options->section_tipo; // $target_section_tipo; // Fichero $xml_options->ar_section_id = $ar_section_id; // Array like [45001,45002,45003]; - $xml_options->save_to_file_path = DEDALO_MEDIA_BASE_PATH . $sub_path . $rdf_file_name; // Target file + $xml_options->save_to_file_path = DEDALO_MEDIA_BASE_PATH . $sub_path . $rdf_file_name; // Target file $xml_options->url_file = DEDALO_MEDIA_BASE_URL . $sub_path . $rdf_file_name; $response = $this->build_xml_file( $xml_options ); #dump($response, ' response ++ '.to_string($options)); - // saves publication data + // saves publication data diffusion::update_publication_data($options->section_tipo, $options->section_id); return $response; @@ -163,7 +163,7 @@ public function update_record( $request_options, $resolve_references=false ) { * @return object $response */ public function build_xml_file( $request_options ) { - + # Maximum execution time seconds set_time_limit(600); @@ -172,7 +172,7 @@ public function build_xml_file( $request_options ) { $response = new stdClass(); $response->result = false; $response->msg = []; - + $options = new stdClass(); $options->xml_tipo = null; $options->xml_validate = true; @@ -180,9 +180,9 @@ public function build_xml_file( $request_options ) { $options->section_tipo = null; $options->ar_section_id = array(); $options->save_to_file_path = false; - $options->url_file = false; + $options->url_file = false; foreach ($request_options as $key => $value) {if (property_exists($options, $key)) $options->$key = $value;} - + # # WRAPPER $xml_tipo = $options->xml_tipo; // Like mupreva2190 for 'Numisma RDF' @@ -199,11 +199,11 @@ public function build_xml_file( $request_options ) { # PARSE RDF_OBJECT $ar_element=array(); - foreach ($options->ar_section_id as $current_section_id) { + foreach ($options->ar_section_id as $current_section_id) { $element = $this->parse_rdf_object($rdf_object, $options->section_tipo, $current_section_id); - #dump($element, ' element ++ $current_section_id: '.to_string($current_section_id)); + #dump($element, ' element ++ $current_section_id: '.to_string($current_section_id)); $ar_element[] = $element; - } + } # INJECT VALUE ON WRAPPER $rdf_wrapper['rdf_value'] = implode("\n", $ar_element); @@ -212,7 +212,7 @@ public function build_xml_file( $request_options ) { $rdf_wrapper_string = implode("\n", $rdf_wrapper); # - # XML. Verify xml format is valid and format output + # XML. Verify xml format is valid and format output $xml_string = self::xml_object($rdf_wrapper_string, $options->xml_validate, $options->xml_format_output); if (!$xml_string) { $response->msg[] = "xml_string error. Bad format"; // .": \n".htmlspecialchars($xml_string); @@ -220,14 +220,14 @@ public function build_xml_file( $request_options ) { }else{ $response->msg[] = ''; // "xml_string created successfully"; // .": \n".htmlspecialchars($xml_string); $response->result = true; //$xml_string; - } + } # # SAVE FILE if ($options->save_to_file_path) { if( file_put_contents($options->save_to_file_path, trim($xml_string)) ){ #$response->msg[] = "File is saved successfully"; - $msg = ''; + $msg = ''; $msg .= '  Download '.pathinfo($options->url_file,PATHINFO_BASENAME).''; if(SHOW_DEBUG===true) { $msg .= '
DEBUG: ' . $options->save_to_file_path; @@ -238,7 +238,7 @@ public function build_xml_file( $request_options ) { // response additional info $response->url = $options->url_file; - + $total_time=round(microtime(1)-$start_time,3); $response->debug[] = "Generated [".count($options->ar_section_id)." elements] in $total_time secs"; @@ -253,7 +253,7 @@ public function build_xml_file( $request_options ) { * @return string $parsed_obj */ public function parse_rdf_object( $rdf_object, $section_tipo, $section_id, $reset=true ) { - + static $parse; if ($reset===true) { @@ -261,18 +261,18 @@ public function parse_rdf_object( $rdf_object, $section_tipo, $section_id, $rese } $service_name = $this->service_name; - + $resolved = (object)$this->resolve_rdf_object( $rdf_object, $section_tipo, $section_id, $service_name ); #dump($rdf_object->propiedades->is_header, ' rdf_object ++ '.to_string()); - #dump($resolved, ' resolved ++ '.to_string()); - + #dump($resolved, ' resolved ++ '.to_string()); + # # IN TAG $parse .= isset($resolved->in_tag) ? $resolved->in_tag : ''; # # DATA INSIDE - $data = $rdf_object->data; + $data = $rdf_object->data; if (empty($data)) { # @@ -281,15 +281,15 @@ public function parse_rdf_object( $rdf_object, $section_tipo, $section_id, $rese # # OUT TAG - $parse .= isset($resolved->out_tag) ? $resolved->out_tag : ''; + $parse .= isset($resolved->out_tag) ? $resolved->out_tag : ''; }else{ - + $n_childrens=count($data); - + $i=1;foreach ($data as $data_obj) { - - + + if ( $rdf_object->modelo_name==='rdf:subject' && $data_obj->modelo_name==='rdf:subject' && !isset($added_close) ) @@ -307,10 +307,10 @@ public function parse_rdf_object( $rdf_object, $section_tipo, $section_id, $rese if ($i===$n_childrens && !isset($added_close) && !empty($rdf_object->ar_related)) { //rdf:predicate $parse .= ">"; //$parse .="YYY[$data_obj->tipo]YYY"; $added_close=true; - } - + } + $i++;}//end foreach ($data as $data_obj) { - + # # VALUE TAG @@ -318,17 +318,17 @@ public function parse_rdf_object( $rdf_object, $section_tipo, $section_id, $rese # # OUT TAG - if ($n_childrens===1 && $data_obj->modelo_name==='rdf:predicate' && empty($rdf_object->ar_related)) { //&& $rdf_object->modelo_name=='rdf:predicate' && empty($rdf_object->ar_related) + if ($n_childrens===1 && $data_obj->modelo_name==='rdf:predicate' && empty($rdf_object->ar_related)) { //&& $rdf_object->modelo_name=='rdf:predicate' && empty($rdf_object->ar_related) $parse .= '/>'; }else{ $parse .= isset($resolved->out_tag) ? $resolved->out_tag : ''; } - + }//end if (!empty($data)) { #dump($parse, ' parse ++ '.to_string()); - - return $parse; + + return $parse; }//end parse_rdf_object @@ -340,7 +340,7 @@ public function parse_rdf_object( $rdf_object, $section_tipo, $section_id, $rese * @return object */ public function build_rdf_object( $rdf_tipo ) { - + $RecordObj_dd = new RecordObj_dd($rdf_tipo); $name = RecordObj_dd::get_termino_by_tipo($rdf_tipo); $modelo_name = RecordObj_dd::get_modelo_name_by_tipo($rdf_tipo,true); @@ -352,17 +352,17 @@ public function build_rdf_object( $rdf_tipo ) { $rdf_object->tipo = $rdf_tipo; $rdf_object->propiedades = $RecordObj_dd->get_propiedades(true); $rdf_object->ar_related = $RecordObj_dd::get_ar_terminos_relacionados($rdf_tipo, $cache=true, $simple=true); - $rdf_object->data = array(); + $rdf_object->data = array(); # - # RDF ELEMENTS + # RDF ELEMENTS foreach ((array)$ar_elements as $current_element_tipo) { $rdf_object->data[] = $this->build_rdf_object( $current_element_tipo ); - - }//end foreach ($ar_elements as $curent_children_tipo) { - return $rdf_object; + }//end foreach ($ar_elements as $curent_children_tipo) { + + return $rdf_object; }//end build_rdf_object @@ -372,14 +372,14 @@ public function build_rdf_object( $rdf_tipo ) { * @return object $resolved */ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $section_id, $service_name ) { - + $resolved = new stdClass(); - + # Clean cloned object $rdf_object = clone($received_rdf_object); #unset($rdf_object->data); $rdf_object->data = count($rdf_object->data); - + $name = $rdf_object->name; $modelo_name = $rdf_object->modelo_name; $tipo = $rdf_object->tipo; @@ -389,8 +389,8 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio $separator = isset($rdf_object->propiedades->separator) ? $rdf_object->propiedades->separator : '/'; $format = isset($rdf_object->propiedades->format) ? $rdf_object->propiedades->format : false; - $entity_section_id = $this->entity_section_id; // Fixed on update - + $entity_section_id = $this->entity_section_id; // Fixed on update + # # RESOLVE BY TYPE $value=''; switch ($type) { @@ -407,7 +407,7 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio $fixed_value = isset($propiedades->value) ? $propiedades->value : null; $value = " $name=\"$fixed_value\""; break; - + case 'fixed_uri': // base_uri_entity (resolve wit entity service data) if (isset($propiedades->base_uri_entity)) { @@ -425,7 +425,7 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio $base_uri = $this->resolve_base_uri_entity($propiedades->base_uri_entity); }else{ $base_uri = isset($propiedades->base_uri) ? $propiedades->base_uri : null; - } + } $media_uri = isset($propiedades->media_uri) ? $propiedades->media_uri : new stdClass(); $quality = $media_uri->quality; @@ -456,8 +456,8 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio $locator->section_tipo, false); $aditional_path = $component_obj->get_aditional_path(); - $image_id = $component_obj->get_target_filename(); - + $image_id = $component_obj->get_target_filename(); + $media_uri_string = $quality . $aditional_path . '/' . $image_id ; }else{ @@ -465,7 +465,7 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio $media_uri_string = $quality .'/undefined' ; } #dump($media_uri_string, ' media_uri_string ++ '.to_string( $component_tipo )); - + $value = ' ' . $name . '="' . rtrim($base_uri,'/') . '/' . $media_uri_string . '"'; break; @@ -478,11 +478,11 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio $base_uri = isset($propiedades->base_uri) ? $propiedades->base_uri : null; } $add_uri = isset($propiedades->add_uri) ? $propiedades->add_uri : array(); - + # iterate elements - $add_uri_string=''; foreach((array)$add_uri as $object_uri) { - - $component_tipo = $object_uri->value; // Default behaviour + $add_uri_string=''; foreach((array)$add_uri as $object_uri) { + + $component_tipo = $object_uri->value; // Default behaviour // component_autocomplete cases overriding default list if (isset($object_uri->source)) { $component_tipo = $object_uri->source; @@ -490,7 +490,7 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio # Resolve value $ct_modelo_name = RecordObj_dd::get_modelo_name_by_tipo($component_tipo,true); - # dump($ct_modelo_name, ' ct_modelo_name - component_tipo:'.$component_tipo.' - type ++ '.to_string($type)); + # dump($ct_modelo_name, ' ct_modelo_name - component_tipo:'.$component_tipo.' - type ++ '.to_string($type)); switch ($ct_modelo_name) { case 'component_section_id': $value = $section_id; @@ -508,7 +508,7 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio # get_valor($lang=DEDALO_DATA_LANG, $format='string', $ar_related_terms=false, $divisor="
") $dato = $component_obj->get_valor(DEDALO_DATA_LANG, 'string', (array)$object_uri->value, ', '); // Ref. $lang=DEDALO_DATA_LANG, $format='string', $ar_related_terms=false $value = $dato; - break; + break; default: $component_obj = component_common::get_instance( $ct_modelo_name, $component_tipo, @@ -520,10 +520,10 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio $dato = $component_obj->get_valor(); $value = $dato; break; - } + } $add_uri_string .= trim($value); - $add_uri_string .= $object_uri!=end($add_uri) ? $separator : ''; - + $add_uri_string .= $object_uri!=end($add_uri) ? $separator : ''; + } if ($modelo_name==='rdf:predicate') { $value = " $name=\"{$base_uri}{$add_uri_string}\""; @@ -532,15 +532,15 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio if(SHOW_DEBUG && isset($pisuerga)) { if ($base_uri==='http://numismatics.org/ocre/id/ric.') { // Disabled - #self::test_method($base_uri, $add_uri_string, $section_id); + #self::test_method($base_uri, $add_uri_string, $section_id); } }//end if(SHOW_DEBUG) { }else{ $value = trim("{$base_uri}{$add_uri_string}"); #$value = '' . rtrim($base_uri,'/') . '/' . $add_uri_string . ''; - } - break; + } + break; case 'var_uri'; // base_uri. Eg. http://domain.com/catalog/ + '?id=14527' @@ -551,9 +551,9 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio $base_uri = isset($propiedades->base_uri) ? $propiedades->base_uri : null; } $var_uri = isset($propiedades->var_uri) ? $propiedades->var_uri : array(); - + $var_uri_string=''; foreach((array)$var_uri as $key => $component_tipo) { - + if ($key==='#') { $value = $component_tipo; @@ -561,14 +561,14 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio $var_uri_string = substr($var_uri_string, 0, -1); } $var_uri_string .= $value; - + }else{ # Resolve value $ct_modelo_name = RecordObj_dd::get_modelo_name_by_tipo($component_tipo,true); if ($ct_modelo_name==='component_section_id') { $value = $section_id; - }else{ + }else{ $ct_modelo_name = RecordObj_dd::get_modelo_name_by_tipo($component_tipo,true); $component_obj = component_common::get_instance( $ct_modelo_name, $component_tipo, @@ -587,17 +587,17 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio } else{ $var_uri_string .= "/$value"; // Rewrite version } - + } - - $var_uri_string .= ($component_tipo!=end($var_uri)) ? '&' : ''; + + $var_uri_string .= ($component_tipo!=end($var_uri)) ? '&' : ''; } #$value = " $name=\"{$base_uri}?{$var_uri_string}\""; $value = " $name=\"{$base_uri}{$var_uri_string}\""; // Rewrite version break; - + default: - // Without type + // Without type foreach ((array)$ar_related as $related_tipo) { # Resolve value $rel_modelo_name = RecordObj_dd::get_modelo_name_by_tipo($related_tipo,true); @@ -623,7 +623,7 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio switch (true) { case ( property_exists($format, 'find_replace') ): #debug_log(__METHOD__." Replacing value (".$format->find_replace->find." => ".$format->find_replace->replace.") in: ".to_string($value), logger::DEBUG); - $value = str_replace($format->find_replace->find, $format->find_replace->replace, $value); + $value = str_replace($format->find_replace->find, $format->find_replace->replace, $value); break; } }//end if ($format) { @@ -639,11 +639,11 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio # IN $resolved->in_tag = ''; $resolved->in_tag .= "\n<$name"; - if($rdf_object->data==0) $resolved->in_tag .= ">"; // Without childrens + if($rdf_object->data==0) $resolved->in_tag .= ">"; // Without childrens # VALUE - $resolved->value_tag = trim($value); - + $resolved->value_tag = trim($value); + # OUT $resolved->out_tag = ''; if($rdf_object->data>0) { @@ -658,11 +658,11 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio } $resolved->out_tag .= ""; break; - + case 'rdf:predicate': $resolved->in_tag = ''; - $resolved->value_tag = $value; - $resolved->out_tag = ''; + $resolved->value_tag = $value; + $resolved->out_tag = ''; break; } @@ -675,11 +675,11 @@ public function resolve_rdf_object( $received_rdf_object, $section_tipo, $sectio /** * BUILD_RDF_WRAPPER * Array of rdf wrapper lines to inject body content at element $rdf_wrapper[rdf_value] - * @return + * @return */ public function build_rdf_wrapper( $rdf_tipo ) { - $ar_lines = array(); + $ar_lines = array(); $RecordObj_dd = new RecordObj_dd($rdf_tipo); $name = RecordObj_dd::get_termino_by_tipo($rdf_tipo); @@ -692,7 +692,7 @@ public function build_rdf_wrapper( $rdf_tipo ) { $rdf_object->tipo = $rdf_tipo; $rdf_object->propiedades = (object)$RecordObj_dd->get_propiedades(true); $rdf_object->ar_related = $RecordObj_dd::get_ar_terminos_relacionados($rdf_tipo, $cache=true, $simple=true); - $rdf_object->data = array(); + $rdf_object->data = array(); # XML LINE $ar_lines['xml'] = $rdf_object->propiedades->value; // Like 'get_ar_childrens_of_this(); foreach ($ar_head_elements as $head_element_tipo) { #dump($head_element_tipo, ' head_element_tipo ++ '.to_string()); @@ -726,13 +726,13 @@ public function build_rdf_wrapper( $rdf_tipo ) { } break; } - }//end foreach ($ar_elements as $children_tipo) { + }//end foreach ($ar_elements as $children_tipo) { # RDF LINE $ar_lines['rdf_in'] = "<".$name.$rdf_predicates.">"; $ar_lines['rdf_value'] = ""; // To fill later - $ar_lines['rdf_out'] = ""; + $ar_lines['rdf_out'] = ""; #dump($ar_lines, '$ar_lines ++ '.to_string()); exit(); $this->rdf_wrapper = $ar_lines; @@ -751,7 +751,7 @@ public static function xml_object( $string, $xml_validate=true, $xml_format_outp #return $string; # # TIDY MODE - /* + /* $config = array( 'indent' => true, 'output-xml' => true, @@ -770,10 +770,10 @@ public static function xml_object( $string, $xml_validate=true, $xml_format_outp $xml->validateOnParse = $xml_validate; // Default: true $xml->loadXML( $string ); $xml->formatOutput = $xml_format_output; // Default: true - #dump($xml, ' xml ++ '.to_string()); + #dump($xml, ' xml ++ '.to_string()); $xml_string = $xml->saveXml(); - + /* validate dtd debug_log(__METHOD__." XML->validate response: ".to_string( $xml->validate() ), logger::DEBUG); if (!$xml->validate()) { @@ -788,14 +788,14 @@ public static function xml_object( $string, $xml_validate=true, $xml_format_outp /** * TEST_METHOD - * @return + * @return */ public static function test_method_DISABLED($base_uri, $add_uri_string, $section_id) { - if ($base_uri!='http://numismatics.org/ocre/id/ric.') return false; - + if ($base_uri!='http://numismatics.org/ocre/id/ric.') return false; + $start_time = microtime(1); - + $url_test = $base_uri.$add_uri_string; /* @@ -803,10 +803,10 @@ public static function test_method_DISABLED($base_uri, $add_uri_string, $section #dump($response, ' response ++ '.to_string($url_test)); */ - $opts = array( - 'http' => array( - 'method'=>"GET", - 'header'=>"Content-Type: text/html; charset=utf-8") + $opts = array( + 'http' => array( + 'method'=>"GET", + 'header'=>"Content-Type: text/html; charset=utf-8") ); $context = stream_context_create($opts); $jsonld_test = $url_test.'.jsonld'; @@ -827,7 +827,7 @@ public static function test_method_DISABLED($base_uri, $add_uri_string, $section 'mupreva2232', $section_id, 'edit', - DEDALO_DATA_NOLAN, + DEDALO_DATA_NOLAN, 'mupreva1', false); $locator = new locator(); @@ -842,10 +842,10 @@ public static function test_method_DISABLED($base_uri, $add_uri_string, $section $msg .= "
$section_id - OK. PAGE EXISTS ($response) $url_test
"; /* $jsonld_test = $url_test.'.jsonld'; - $opts = array( - 'http' => array( - 'method'=>"GET", - 'header'=>"Content-Type: text/html; charset=utf-8") + $opts = array( + 'http' => array( + 'method'=>"GET", + 'header'=>"Content-Type: text/html; charset=utf-8") ); $context = stream_context_create($opts); $content_data = file_get_contents($jsonld_test,false,$context); @@ -880,7 +880,7 @@ public static function test_method_DISABLED($base_uri, $add_uri_string, $section $total=round(microtime(1)-$start_time,3); $msg .= " Time: $total"; - + logger::$obj['error']->log_message($msg, logger::ERROR, __METHOD__); echo $msg; @@ -890,10 +890,10 @@ public static function test_method_DISABLED($base_uri, $add_uri_string, $section /** * DIFFUSION_COMPLETE_DUMP - * @return + * @return */ public function diffusion_complete_dump($diffusion_element, $resolve_references = true) { - + // Working here debug_log(__METHOD__." Called unfinished class. Nothing is done ".to_string(), logger::DEBUG); }//end diffusion_complete_dump @@ -902,7 +902,7 @@ public function diffusion_complete_dump($diffusion_element, $resolve_references /** * GET_DIFFUSION_ELEMENT_TABLES_MAP - * @return + * @return */ public function get_diffusion_element_tables_map() { @@ -910,11 +910,11 @@ public function get_diffusion_element_tables_map() { # Working here - return $diffusion_element_tables_map; + return $diffusion_element_tables_map; }//end get_diffusion_element_tables_map - + /** * PARSE_RDF_CONFIG_VAR * Parse variable of type rdf_config::my_value to config value @@ -924,15 +924,15 @@ public function parse_rdf_config_var($rdf_var) { $rdf_config_prefix = 'rdf_config::'; if (strpos($rdf_var, $rdf_config_prefix)!==false) { - + $rdf_config = (object)RDF_CONFIG; // Variable from config case $config_name = str_replace($rdf_config_prefix, '', $rdf_var); $rdf_var = $rdf_config->{$config_name}; - } - - + } + + return $rdf_var; }//end parse_rdf_config_var */ @@ -944,12 +944,12 @@ public function parse_rdf_config_var($rdf_var) { * @return string $base_uri */ public function resolve_base_uri_entity($base_uri_entity, $section_id=null) { - + // Search Dedalo entities publication services $section_tipo = $base_uri_entity->section_tipo; // services_section_tipo = 'dd1010'; $title = $base_uri_entity->title; - $component_tipo = $base_uri_entity->component_tipo; // component_iri dd1014 - + $component_tipo = $base_uri_entity->component_tipo; // component_iri dd1014 + if ($section_tipo===DEDALO_SERVICES_SECTION_TIPO) { // Case search in private services section $entity_id = DEDALO_ENTITY_ID; // Config DEDALO_ENTITY_ID @@ -1009,29 +1009,29 @@ public function resolve_base_uri_entity($base_uri_entity, $section_id=null) { // search $search_development2 = new search_development2($search_query_object); $result = $search_development2->search(); - $row = reset($result->ar_records); - + $row = reset($result->ar_records); + // base_uri if (empty($row)) { - + $base_uri = null; debug_log(__METHOD__." Empty records! Nothing is found for entity: '$entity_id' and service_name: '$service_name' ".to_string(), logger::ERROR); - + }else{ - - $iri_object_data = json_decode($row->{$component_tipo}); + + $iri_object_data = json_decode($row->{$component_tipo}); $ar_result = array_filter((array)$iri_object_data, function($item) use($title){ return $item->title === $title; }); - $result = reset($ar_result); + $result = reset($ar_result); $base_uri = !empty($result->iri) ? $result->iri : null; - } + } }else{ // Case search in public entity section - // Collection (Entity) + // Collection (Entity) $modelo_name = RecordObj_dd::get_modelo_name_by_tipo($base_uri_entity->from_component_tipo,true); $component = component_common::get_instance($modelo_name, @@ -1041,8 +1041,8 @@ public function resolve_base_uri_entity($base_uri_entity, $section_id=null) { DEDALO_DATA_NOLAN, $base_uri_entity->from_section_tipo); $dato_entity = $component->get_dato(); - - if (!empty($dato_entity)) { + + if (!empty($dato_entity)) { // component load $modelo_name = RecordObj_dd::get_modelo_name_by_tipo($component_tipo,true); $component = component_common::get_instance($modelo_name, @@ -1051,28 +1051,28 @@ public function resolve_base_uri_entity($base_uri_entity, $section_id=null) { 'list', DEDALO_DATA_NOLAN, $section_tipo); - $dato = $component->get_dato(); + $dato = $component->get_dato(); } - + // base_uri if (empty($dato)) { - + $base_uri = null; debug_log(__METHOD__." Empty dato! Nothing is found for section_id: '$section_id' - section_tipo: '$section_tipo' - component_tipo: ".to_string($component_tipo), logger::ERROR); - + }else{ - $iri_object_data = (array)$dato; + $iri_object_data = (array)$dato; $ar_result = array_filter((array)$iri_object_data, function($item) use($title){ - return $item->title === $title; + return isset($item->title) && $item->title===$title; }); - + $base_uri = isset($ar_result[0]) ? $ar_result[0]->iri : null; } } #dump($base_uri, ' base_uri ++ '.to_string()); - + return $base_uri; }//end resolve_base_uri_entity @@ -1085,22 +1085,22 @@ public function resolve_base_uri_entity($base_uri_entity, $section_id=null) { * @return array $ar_diffusion_sections */ public static function get_diffusion_sections_from_diffusion_element($diffusion_element_tipo) { - + $ar_diffusion_sections = array(); # xml elements $elements = RecordObj_dd::get_ar_terminoID_by_modelo_name_and_relation($diffusion_element_tipo, 'xml', 'children', true); foreach ($elements as $current_element_tipo) { - + # Pointer to section $ar_related = common::get_ar_related_by_model('section', $current_element_tipo); - + if (isset($ar_related[0])) { $ar_diffusion_sections[] = $ar_related[0]; } } - + return $ar_diffusion_sections; }//end get_diffusion_sections_from_diffusion_element @@ -1111,12 +1111,12 @@ public static function get_diffusion_sections_from_diffusion_element($diffusion_ * @return array $rows *//* public static function get_target_rows($target_section_tipo, $section_tipo, $section_id) { - + $query = ' { "id": "numisdata4_list", "section_tipo": "'.$target_section_tipo.'", - "limit": false, + "limit": false, "filter": { "$and": [ { @@ -1171,7 +1171,7 @@ public static function get_target_rows($target_section_tipo, $section_tipo, $sec * @return array $ar_section_id_clean */ public static function get_to_publish_rows($section_tipo, $ar_section_id) { - + $q = implode(',', (array)$ar_section_id); // query @@ -1179,7 +1179,7 @@ public static function get_to_publish_rows($section_tipo, $ar_section_id) { { "id": "numisdata4_list", "section_tipo": "'.$section_tipo.'", - "limit": false, + "limit": false, "filter": { "$and": [ { @@ -1229,4 +1229,4 @@ public static function get_to_publish_rows($section_tipo, $ar_section_id) { }//end class -?> \ No newline at end of file +?> diff --git a/lib/dedalo/extras/numisdata/widgets/get_archive_weights/css/get_archive_weights.css b/lib/dedalo/extras/numisdata/widgets/get_archive_weights/css/get_archive_weights.css index 4a8e38a977..f538b9c17f 100644 --- a/lib/dedalo/extras/numisdata/widgets/get_archive_weights/css/get_archive_weights.css +++ b/lib/dedalo/extras/numisdata/widgets/get_archive_weights/css/get_archive_weights.css @@ -4,6 +4,7 @@ font-size: 120%; } .get_archive_weights { + padding-right: 5px; color: #555555; font-family: "Arial"; } @@ -12,4 +13,8 @@ position: absolute; right: 0; } +.get_archive_diameter { + border-left: 1px solid #c8c8c8; + padding-left: 5px; +} /*# sourceMappingURL=get_archive_weights.css.map */ \ No newline at end of file diff --git a/lib/dedalo/extras/numisdata/widgets/get_archive_weights/css/get_archive_weights.css.map b/lib/dedalo/extras/numisdata/widgets/get_archive_weights/css/get_archive_weights.css.map index ba181baebe..3051a0d548 100644 --- a/lib/dedalo/extras/numisdata/widgets/get_archive_weights/css/get_archive_weights.css.map +++ b/lib/dedalo/extras/numisdata/widgets/get_archive_weights/css/get_archive_weights.css.map @@ -1 +1 @@ -{"version":3,"sources":["get_archive_weights.less"],"names":[],"mappings":";;AAOC;EAEC,eAAA;;AAGD;EAGC,cAAA;EACA,aAAa,OAAb;;AAED;EACA,UAAA;EACA,kBAAA;EACA,QAAA","file":"get_archive_weights.css"} \ No newline at end of file +{"version":3,"sources":["get_archive_weights.less"],"names":[],"mappings":";;AAOC;EAEC,eAAA;;AAGD;EACC,kBAAA;EAEA,cAAA;EACA,aAAa,OAAb;;AAED;EACA,UAAA;EACA,kBAAA;EACA,QAAA;;AAGA;EACC,8BAAA;EACA,iBAAA","file":"get_archive_weights.css"} \ No newline at end of file diff --git a/lib/dedalo/extras/numisdata/widgets/get_archive_weights/css/get_archive_weights.less b/lib/dedalo/extras/numisdata/widgets/get_archive_weights/css/get_archive_weights.less index 73bc897d70..b253930ba7 100644 --- a/lib/dedalo/extras/numisdata/widgets/get_archive_weights/css/get_archive_weights.less +++ b/lib/dedalo/extras/numisdata/widgets/get_archive_weights/css/get_archive_weights.less @@ -11,7 +11,7 @@ //padding-left: 15px; } .get_archive_weights { - //padding-left: 10px; + padding-right: 5px; //font-size: 80%; color: #555555; font-family: "Arial"; @@ -22,3 +22,9 @@ right: 0; } + .get_archive_diameter{ + border-left: 1px solid rgb(200, 200, 200); + padding-left: 5px; + + } + diff --git a/lib/dedalo/extras/numisdata/widgets/get_archive_weights/get_archive_weights.php b/lib/dedalo/extras/numisdata/widgets/get_archive_weights/get_archive_weights.php index 87e20e6ff1..bfd87bbe35 100644 --- a/lib/dedalo/extras/numisdata/widgets/get_archive_weights/get_archive_weights.php +++ b/lib/dedalo/extras/numisdata/widgets/get_archive_weights/get_archive_weights.php @@ -4,7 +4,7 @@ $widget_name = $this->widget_name; $modo = $this->component_info->get_modo(); - $section_id = $this->component_info->get_parent(); + $section_id = $this->component_info->get_parent(); $section_tipo = $this->component_info->get_section_tipo(); $data_source = $this->data_source; $filename = $modo; @@ -21,7 +21,7 @@ css::$ar_url[] = $widget_base_url ."/css/".$widget_name.".css"; if($modo==='edit') { - js::$ar_url[] = $widget_base_url ."/js/".$widget_name.".js"; + js::$ar_url[] = $widget_base_url ."/js/".$widget_name.".js"; } $component_source = array_reduce($data_source, function ($carry, $item){ @@ -51,7 +51,7 @@ return 'Empty portal data'; } - + $component_used = array_reduce($data_source, function ($carry, $item){ @@ -65,20 +65,31 @@ $section_tipo_used = $component_used->section_tipo; - $component_data = array_reduce($data_source, function ($carry, $item){ + $component_data_weights = array_reduce($data_source, function ($carry, $item){ + + if ($item->type==='data_weights') { + return $item; + } + return $carry; + }); + + $component_tipo_data_weights = $component_data_weights->component_tipo; + + + $component_data_diameter = array_reduce($data_source, function ($carry, $item){ - if ($item->type==='data') { + if ($item->type==='data_diamenter') { return $item; } return $carry; }); - $component_tipo_data = $component_data->component_tipo; - $section_tipo_data = $component_data->section_tipo; + $component_tipo_data_diameter = $component_data_diameter->component_tipo; $weights = []; + $diameter = []; #get the value of the component using portal dato foreach ($dato as $current_locator) { @@ -95,30 +106,59 @@ $used_dato = $used->get_dato(); - if ($used_dato[0]->section_id === '2') continue; + #if ($used_dato[0]->section_id==='2') continue; + if (empty($used_dato) || $used_dato[0]->section_id==='2') continue; + //weights + $data_weights_modelo_name = RecordObj_dd::get_modelo_name_by_tipo($component_tipo_data_weights,true); // Expected portal + $data_weights = component_common::get_instance($data_weights_modelo_name, + $component_tipo_data_weights, + $section_id, + 'list', + DEDALO_DATA_NOLAN, + $section_tipo); + + $data_weights_dato = $data_weights->get_dato(); + $data_weights_label = $data_weights->get_label(); - $data_modelo_name = RecordObj_dd::get_modelo_name_by_tipo($component_tipo_data,true); // Expected portal - $data = component_common::get_instance($data_modelo_name, - $component_tipo_data, + if(!empty($data_weights_dato)){ + $weights[] = $data_weights_dato; + } + + //diameter + $data_diameter_modelo_name = RecordObj_dd::get_modelo_name_by_tipo($component_tipo_data_diameter,true); // Expected portal + $data_diameter = component_common::get_instance($data_diameter_modelo_name, + $component_tipo_data_diameter, $section_id, 'list', DEDALO_DATA_NOLAN, $section_tipo); - $data_dato = $data->get_dato(); + $data_diameter_dato = $data_diameter->get_dato(); + $data_diameter_label = $data_diameter->get_label(); - if(!empty($data_dato)){ - $weights[] = $data_dato; + if(!empty($data_diameter_dato)){ + $diameter[] = $data_diameter_dato; } - } - $media_weight = round((array_sum($weights) / count($weights)),2); - $total_elements = count($weights); - $max_weight = max($weights); - $min_weight = min($weights); + if (!empty($weights)) { + $media_weight = round((array_sum($weights) / count($weights)),2); + $total_elements_weights = count($weights); + $max_weight = max($weights); + $min_weight = min($weights); + }else{ + debug_log(__METHOD__." Empty weights. Sum ignored in widget get_archive_weights ".to_string(), logger::DEBUG); + } + if (!empty($diameter)) { + $media_diameter = round((array_sum($diameter) / count($diameter)),2); + $total_elements_diameter = count($diameter); + $max_diameter = max($diameter); + $min_diameter = min($diameter); + }else{ + debug_log(__METHOD__." Empty diameter. Sum ignored in widget get_archive_weights ".to_string(), logger::DEBUG); + } break; default: @@ -128,7 +168,7 @@ - $page_html = dirname(__FILE__) . '/html/' . $widget_name . '_' . $filename . '.phtml'; + $page_html = dirname(__FILE__) . '/html/' . $widget_name . '_' . $filename . '.phtml'; if( !include($page_html) ) { echo "
Invalid widget mode $modo
"; } diff --git a/lib/dedalo/extras/numisdata/widgets/get_archive_weights/html/get_archive_weights_edit.phtml b/lib/dedalo/extras/numisdata/widgets/get_archive_weights/html/get_archive_weights_edit.phtml index bab02b37ca..c0cd29573f 100644 --- a/lib/dedalo/extras/numisdata/widgets/get_archive_weights/html/get_archive_weights_edit.phtml +++ b/lib/dedalo/extras/numisdata/widgets/get_archive_weights/html/get_archive_weights_edit.phtml @@ -1,41 +1,74 @@ "; - + # # SUM_INTERVALS TO TEXT $html .= "
"; - if($media_weight>0) { + if(isset($media_weight) && $media_weight>0) { //$add_label = label::get_label('average', $lang).': '; - //$html .= ' '.$add_label.' '; + $html .= ' '.$data_weights_label.': '; $html .= $media_weight; } $html .= "
";//end sum_dates_total $html .= ""; - if($max_weight>0) { + if(isset($max_weight) && $max_weight>0) { // $add_label = label::get_label('max', $lang).'max: ';; $add_label = 'max: '; $html .= ' '.$add_label .' '; $html .= $max_weight; } - if($min_weight>0) { + if(isset($min_weight) && $min_weight>0) { //$add_label = label::get_label('min', $lang).'min: '; $add_label = ' | min: '; $html .= ' '.$add_label.' '; $html .= $min_weight; } - if($total_elements>0) { + if(isset($total_elements_weights) && $total_elements_weights>0) { $add_label = ' | n: '; $html .= ' '.$add_label.' '; - $html .= $total_elements; + $html .= $total_elements_weights; } $html .= "";//end sum_dates_total $html .= ""; - print $html; -?> \ No newline at end of file + + $html .= "
"; + + # + # SUM_INTERVALS TO TEXT + $html .= "
"; + if(isset($media_diameter) && $media_diameter>0) { + //$add_label = label::get_label('average', $lang).': '; + $html .= ' '.$data_diameter_label.': '; + $html .= $media_diameter; + } + $html .= "
";//end sum_dates_total + $html .= ""; + if(isset($max_diameter) && $max_diameter>0) { + // $add_label = label::get_label('max', $lang).'max: ';; + $add_label = 'max: '; + $html .= ' '.$add_label .' '; + $html .= $max_diameter; + } + if(isset($min_diameter) && $min_diameter>0) { + //$add_label = label::get_label('min', $lang).'min: '; + $add_label = ' | min: '; + $html .= ' '.$add_label.' '; + $html .= $min_diameter; + } + if(isset($total_elements_diameter) && $total_elements_diameter>0) { + $add_label = ' | n: '; + $html .= ' '.$add_label.' '; + $html .= $total_elements_diameter; + } + $html .= "";//end sum_dates_total + + $html .= "
"; + + print $html; diff --git a/lib/dedalo/services/service_autocomplete/js/service_autocomplete.js b/lib/dedalo/services/service_autocomplete/js/service_autocomplete.js index 343cdd89fb..a44b59f758 100644 --- a/lib/dedalo/services/service_autocomplete/js/service_autocomplete.js +++ b/lib/dedalo/services/service_autocomplete/js/service_autocomplete.js @@ -1,6 +1,6 @@ /** * SERVICE_AUTOCOMPLETE -* Used as service by component_autocomplete, component_autocomplete_hi, +* Used as service by component_autocomplete, component_autocomplete_hi, * component_relation_parent, component_relation_children, component_relation_related * */ @@ -19,9 +19,9 @@ var service_autocomplete = new function() { * @return bool */ this.init = function(request_options) { - + const self = this - + const options = { component_js : null, autocomplete_wrapper : null @@ -39,8 +39,8 @@ var service_autocomplete = new function() { alert("Error. Invalid wrapper"); return false } - - // Build_autocomplete_input input + + // Build_autocomplete_input input const input_obj = self.build_autocomplete_input({ parent : wrapper }) @@ -64,10 +64,10 @@ var service_autocomplete = new function() { this.build_autocomplete_input = function(options) { const self = this - + const input_obj = common.create_dom_element({ element_type : "input", - class_name : "autocomplete_input", // css_autocomplete_hi_search_field + class_name : "autocomplete_input", // css_autocomplete_hi_search_field parent : options.parent }) input_obj.setAttribute("type", "text") @@ -75,10 +75,10 @@ var service_autocomplete = new function() { input_obj.setAttribute("autocomplete", "off") input_obj.setAttribute("autocorrect", "off") - // - + return input_obj };//end build_autocomplete_input @@ -90,7 +90,7 @@ var service_autocomplete = new function() { */ let cache = {}; this.activate = function( input_obj, component_js ) { - + const self = this // wrap_div . From component wrapper @@ -106,13 +106,13 @@ var service_autocomplete = new function() { const propiedades = component_info.propiedades || {} const wrap_id = wrap_div.dataset.section_tipo +'_'+ wrap_div.dataset.tipo+'_'+wrap_div.dataset.parent - // Custom events defined in propiedades + // Custom events defined in propiedades const custom_events = (propiedades.custom_events) ? propiedades.custom_events : [] if(SHOW_DEBUG===true) { console.log("[service_autocomplete.activate] custom_events:",custom_events) } - + $(input_obj).autocomplete({ delay : 300, minLength : component_js.min_length || 1, @@ -127,7 +127,7 @@ var service_autocomplete = new function() { // response(cache[uid]) // return; //} - + // Request term const q = request.term // Get wrap_div base search_query_object and updates with user input value @@ -145,13 +145,13 @@ var service_autocomplete = new function() { search_engine = selector_source.search_engine; const original_ar_elements = JSON.parse(source_selector.dataset.source); selected_fields = original_ar_elements.ar_elements.filter(element => element.section_tipo===selector_source.section_tipo) - } + } const search_query_object = self.rebuild_search_query_object2(wrap_div, q); - - + + // Search - const search_options = { + const search_options = { component_tipo : wrap_div.dataset.tipo, section_tipo : wrap_div.dataset.section_tipo, divisor : wrap_div.dataset.divisor || " | ", @@ -160,16 +160,16 @@ var service_autocomplete = new function() { search_engine : search_engine, selected_fields : selected_fields, q : q - } + } self.autocomplete_search(search_options) .then(function(response_data){ if(SHOW_DEBUG===true) { console.log("[service_autocomplete.activate] response_data:",response_data); } - + // Format result for use in jquery label / value const label_value = self.convert_data_to_label_value(response_data) - + // Exec response callback for jquery source response(label_value) }) @@ -184,7 +184,7 @@ var service_autocomplete = new function() { // Custom behavior for (let i = 0; i < custom_events_select.length; i++) { - const fn = custom_events_select[i].select + const fn = custom_events_select[i].select component_js[fn]({ event : event, ui : ui, @@ -194,7 +194,7 @@ var service_autocomplete = new function() { wrap_div : wrap_div }) } - + }else{ // Default behavior @@ -230,9 +230,9 @@ var service_autocomplete = new function() { }, response: function( event, ui ) { } - });//end $(this).autocomplete({ + });//end $(this).autocomplete({ - return true + return true };//end this.activate @@ -244,25 +244,25 @@ var service_autocomplete = new function() { * } */ this.autocomplete_search = function(search_options){ - + // check if there are selected values as section_tipo if (!search_options.search_query_object.section_tipo || search_options.search_query_object.section_tipo.length<1) { return new Promise(function(resolve, reject) { console.warn("Empty selected values as section. Ignored search!") resolve(false) - }) + }) } // search source selector let search_engine = (search_options.search_engine !== null) ? search_options.search_engine : "search_dedalo"; - + if(SHOW_DEBUG===true) { - console.log("[service-autocomplete.autocomplete_search] search_engine:",search_engine); //return - } + console.log("[service-autocomplete.autocomplete_search] search_engine:",search_engine); //return + } // exec search const js_promise = this[search_engine](search_options) - + // test //const js_promise = this["search_zenon"](search_options) // console.log("js_promise:",js_promise); @@ -274,15 +274,15 @@ var service_autocomplete = new function() { /** * SEARCH_DEDALO - * @return promise + * @return promise */ this.search_dedalo = function(search_options) { - + // trigger vars const url_trigger = DEDALO_LIB_BASE_URL + "/services/service_autocomplete/trigger.service_autocomplete.php" const trigger_vars = { mode : 'autocomplete_search', - component_tipo : search_options.component_tipo, + component_tipo : search_options.component_tipo, section_tipo : search_options.section_tipo, top_tipo : page_globals.top_tipo, divisor : search_options.divisor || " | ", @@ -315,10 +315,10 @@ var service_autocomplete = new function() { /** * SEARCH_ZENON - * @return + * @return */ this.search_zenon = function(search_options) { - + const self = this if(SHOW_DEBUG===true) { @@ -344,18 +344,18 @@ var service_autocomplete = new function() { console.log("+++ data 1:",data); //console.log("+++ search_options 1:",search_options); //console.log("+++ source 1:",source); - } + } const data_formatted = [] const records = data.records || [] const records_length = records.length - for (let i = 0; i < records_length; i++) { - + for (let i = 0; i < records_length; i++) { + const record = records[i] const ar_value = [] for (let j = 0; j < fields_length; j++) { - - const field = fields[j] + + const field = fields[j] switch(field) { case 'authors': // console.log("++ authors:",record[field]); @@ -364,11 +364,11 @@ var service_autocomplete = new function() { const secondary = record[field].secondary const corporate = record[field].corporate const authors_separator = " - " - + if(SHOW_DEBUG===true) { //console.log("primary:",primary); console.log("secondary:",secondary); console.log("corporate:",corporate); } - + if (Object.keys(primary).length > 0) { authors_ar_value.push(Object.keys(primary).join(authors_separator)) } @@ -388,7 +388,7 @@ var service_autocomplete = new function() { }else{ if (record[field].length>0) { ar_value.push(record[field]) - } + } } break; } @@ -420,24 +420,24 @@ var service_autocomplete = new function() { if(SHOW_DEBUG===true) { console.log("+++ data_formatted 2:",data_formatted); } - + return data_formatted - } - + } + // trigger vars const url_trigger = "https://zenon.dainst.org/api/v1/search" const trigger_vars = { lookfor : q, - type : "AllFields", // search in all fields + type : "AllFields", // search in all fields sort : "relevance", limit : 20, prettyPrint : false, - lng : "de" - }; // console.log("*** [search_zenon] trigger_vars", trigger_vars, search_options) + lng : "de" + }; // console.log("*** [search_zenon] trigger_vars", trigger_vars, search_options) let url_arguments = build_url_arguments_from_vars( trigger_vars ) // const fields = ["id","authors","title","urls","publicationDates"] - for (let i = 0; i < fields_length; i++) { + for (let i = 0; i < fields_length; i++) { url_arguments += "&field[]=" + fields[i] } @@ -463,7 +463,7 @@ var service_autocomplete = new function() { request.onload = function(e) { if (request.status === 200) { - // data format + // data format const data = format_data(request.response) // If successful, resolve the promise by passing back the request response @@ -485,7 +485,7 @@ var service_autocomplete = new function() { // send the request request.send(); - })//end Promise + })//end Promise };//end search_zenon @@ -496,7 +496,7 @@ var service_autocomplete = new function() { * @return bool true */ this.on_select = function(options) { - + const self = this // Prevent set selected value to autocomplete input @@ -534,26 +534,26 @@ var service_autocomplete = new function() { // search_query_object base stored in wrapper dataset const search_query_object = JSON.parse(wrap_div.dataset.search_query_object) - + // Check if filter_sections exists (autocomplete hi) const search_sections = self.get_filter_elements(wrap_div) - + // Update sections property in search_query_object search_query_object.section_tipo = search_sections - + const operator = Object.keys(search_query_object.filter)[0] || '$and' const key = 0 - + //const input = wrap_div.querySelector("input.autocomplete_input") //const q = input.value - + // Update q property search_query_object.filter[operator][key].q = q + "*" // Begins with search_query_object.filter[operator][key].q_split = false // Update wrapper dataset wrap_div.dataset.search_query_object = JSON.stringify(search_query_object) - + if(SHOW_DEBUG===true) { //console.log("search_query_object:",wrap_div.dataset.search_query_object); console.log("[service_autocomplete.rebuild_search_query_object] final filter ",search_query_object.filter); @@ -587,7 +587,7 @@ var service_autocomplete = new function() { const filter_fields_len = filter_fields.length if (q!=="manual_trigger") { - + // Reset filter fields for (let j = 0; j < filter_fields_len; j++) { filter_fields[j].value = '' @@ -600,30 +600,30 @@ var service_autocomplete = new function() { // PROPAGATE TO FILTER FIELDS for (let j = 0; j < filter_fields_len; j++) { if (ar_q[j]) { - let q = ar_q[j] + let q = ar_q[j] let q_type = 'all'//self.determine_q_type(q) - self.propagate_to_filter_field(filter_fields[j], ar_q[j], q_type) - } + self.propagate_to_filter_field(filter_fields[j], ar_q[j], q_type) + } } - }else{ + }else{ for (let i = 0; i < ar_q.length; i++) { let q = ar_q[i] let q_type = self.determine_q_type(q) // PROPAGATE TO FILTER FIELDS for (let j = 0; j < filter_fields_len; j++) { - self.propagate_to_filter_field(filter_fields[j], q, q_type) - } + self.propagate_to_filter_field(filter_fields[j], q, q_type) + } } - } + } };//end if (q!=="manual_trigger") - + // input data - const filter_fields_data = {} - for (let i = 0; i < filter_fields_len; i++) { + const filter_fields_data = {} + for (let i = 0; i < filter_fields_len; i++) { const current_tipo = filter_fields[i].dataset.tipo filter_fields_data[current_tipo] = filter_fields[i].value } - + // filter_by_list let filter_by_list_tipo = false let filter_by_list = JSON.parse(wrap_div.dataset.filter_by_list) @@ -631,23 +631,23 @@ var service_autocomplete = new function() { const filter_by_list_len = filter_by_list.length for (let i = 0; i < filter_by_list_len; i++) { filter_by_list_tipo = filter_by_list[i].component_tipo - } + } } // Selector operator - const operator_selector = wrap_div.querySelector(".operator_selector") + const operator_selector = wrap_div.querySelector(".operator_selector") let operator_selected = (operator_selector) ? "$" + operator_selector.value : "$or"; // Selector split - const split_selector = wrap_div.querySelector(".split_selector") + const split_selector = wrap_div.querySelector(".split_selector") let split_selected = (split_selector) ? JSON.parse(split_selector.value) : true; - + let clean_group = [] // Iterate filter elements let filter_element = search_query_object.filter for (let operator in filter_element) { - //console.log("operator, filter_element[operator]", operator, filter_element[operator]); + //console.log("operator, filter_element[operator]", operator, filter_element[operator]); let group = filter_element[operator] let sub_group2 = {} @@ -662,31 +662,31 @@ var service_autocomplete = new function() { // q_split search_unit.q_split = split_selected - + if (filter_sections_len>0 && base_component_tipo===filter_by_list_tipo) { - - let sub_group1 = {'$or':[]} + + let sub_group1 = {'$or':[]} for (let k = 0; k < filter_sections_len; k++) { let locator = filter_sections[k] let new_search_unit = cloneDeep(search_unit) new_search_unit.q = JSON.stringify(locator) // Remove all path filter_elements but first - new_search_unit.path.splice(1) + new_search_unit.path.splice(1) // Add to group sub_group1['$or'].push(new_search_unit) } clean_group.push(sub_group1) - + }else{ - + if (typeof filter_fields_data[current_component_tipo]!=="undefined") { let value = filter_fields_data[current_component_tipo] - // If value is empty will be ignored in final object + // If value is empty will be ignored in final object if (value.length>0) { // Override q value - search_unit.q = value + search_unit.q = value // When filter is not empy, send search_unit to a special subgroup if (filter_sections_len>0) { // Add to subgroup2 @@ -695,16 +695,16 @@ var service_autocomplete = new function() { // Add to subgroup clean_group.push(search_unit) } - };//end if (value.length>0) + };//end if (value.length>0) } } - + })//end group.forEach(function(search_unit, i) - + // When filter is not empy, add filled sub_group2 if (filter_sections_len>0) { clean_group.push(sub_group2) - } + } //console.log(clean_group); // Overwrite filter value with modified group @@ -712,11 +712,11 @@ var service_autocomplete = new function() { break; // Only one is expected in fisrt level };//end for (let operator in filter_element) { - + // When filter_sections is not empy, use operator '$and' to exclude results by filter_sections if (filter_sections_len>0) { // Force and - operator_selected = '$and' + operator_selected = '$and' } // New clean final filter @@ -742,27 +742,27 @@ var service_autocomplete = new function() { * @return bool */ this.rebuild_search_query_object2 = function(wrap_div, q) { - + const self = this // search_query_object base stored in wrapper dataset const search_query_object = JSON.parse(wrap_div.dataset.search_query_object) if(SHOW_DEBUG===true) { - //console.log("search_query_object:",search_query_object); + console.log("search_query_object:",search_query_object); } // search_sections. Mandatory. Always are defined, in a custom ul/li list or as default using wrapper dataset 'search_sections' - const search_sections = self.get_search_sections(wrap_div) + const search_sections = self.get_search_sections(wrap_div) // filter_by_field_list. Optional. Uses propiedades config params const filter_by_field_list_tipo = self.get_filter_by_field_list_tipo(wrap_div) const filter_by_field_list_value = self.get_filter_by_field_list_value(wrap_div) - const filter_by_field_list_value_len = filter_by_field_list_value ? filter_by_field_list_value.length : 0; - + const filter_by_field_list_value_len = filter_by_field_list_value ? filter_by_field_list_value.length : 0; + // filter_fields_data. Optional. Used when you give the user control to the search fields and operators (like component autocomplete) const filter_fields_data = self.get_filter_fields_data(wrap_div, q) if(SHOW_DEBUG===true) { - //console.log("==== filter_fields_data:",filter_fields_data); + //console.log("==== filter_fields_data:",filter_fields_data); } // rebuild search_query_object filter @@ -776,19 +776,19 @@ var service_autocomplete = new function() { } // operator. Selector operator - const operator_selector = wrap_div.querySelector(".operator_selector") + const operator_selector = wrap_div.querySelector(".operator_selector") let operator_selected = (operator_selector) ? "$" + operator_selector.value : "$or"; // split. Selector split - const split_selector = wrap_div.querySelector(".split_selector") + const split_selector = wrap_div.querySelector(".split_selector") let split_selected = (split_selector) ? JSON.parse(split_selector.value) : true; - + let clean_group = [] let filter_element = search_query_object.filter // Iterate current filter for (let operator in filter_element) { - + // Current group let group = filter_element[operator] @@ -800,7 +800,7 @@ var service_autocomplete = new function() { group.forEach(function(search_unit, i) { const base_component_tipo = search_unit.path ? search_unit.path[0].component_tipo : false const last_component_tipo = search_unit.path ? search_unit.path[search_unit.path.length-1].component_tipo : false - + // q_split search_unit.q_split = split_selected @@ -818,18 +818,18 @@ var service_autocomplete = new function() { sub_group1['$or'].push(new_search_unit) } clean_group.push(sub_group1) - + }else{ // Add fields values to filter if (filter_fields_data && typeof filter_fields_data[last_component_tipo]!=="undefined") { let value = filter_fields_data[last_component_tipo] || [] // Always use 'last_component_tipo' here (!) - + // If value is empty will be ignored in final object if (value.length>0) { - + // Override q value - search_unit.q = value + search_unit.q = value // When filter is not empy, send search_unit to a special subgroup if (filter_by_field_list_value_len>0) { // Add to subgroup2 @@ -838,10 +838,10 @@ var service_autocomplete = new function() { // Add to subgroup clean_group.push(search_unit) } - };//end if (value.length>0) + };//end if (value.length>0) } } - + })//end group.forEach(function(search_unit, i) // When filter is not empy, add filled sub_group2 @@ -868,35 +868,35 @@ var service_autocomplete = new function() { // Replaces old filter in search_query_object search_query_object.filter = clean_filter - + }else{ - let filter_element = search_query_object.filter + let filter_element = search_query_object.filter // Iterate current filter for (let operator in filter_element) { const current_filter = filter_element[operator] for (var i = 0; i < current_filter.length; i++) { - + // Update q property current_filter[i].q = q + "*" // Begins with current_filter[i].q_split = false - + } - + } // Default basic mode (autocomplete hi) //const operator = Object.keys(search_query_object.filter)[0] || '$and' //const key = 0 - - + + // Update wrapper dataset (only in this modo) - // wrap_div.dataset.search_query_object = JSON.stringify(search_query_object) + // wrap_div.dataset.search_query_object = JSON.stringify(search_query_object) } // allow_sub_select_by_id set to false to allow select deep fields - search_query_object.allow_sub_select_by_id = false + search_query_object.allow_sub_select_by_id = false // Debug if(SHOW_DEBUG===true) { @@ -904,19 +904,19 @@ var service_autocomplete = new function() { //console.log("... search_query_object filter:",search_query_object.filter); //if(typeof clean_filter!=="undefined") console.log("+++ rebuild_search_query_object final clean_filter ",clean_filter); } - - return search_query_object + + return search_query_object };//end rebuild_search_query_object2 - + /** * GET_SEARCH_SECTIONS * @return array of checked hierarchy_sections sections */ this.get_search_sections = function(wrap_div) { - + let selected_values = [] // Default const search_sections_list_ul = wrap_div.querySelector('ul.search_sections_list') @@ -933,22 +933,33 @@ var service_autocomplete = new function() { selected_values.push(ar_inputs[i].value) } } - } + } - // Fallback to dataset + // fallback if empty if (selected_values.length<1) { - // Get from wrapper dataset - const search_sections = wrap_div.dataset.search_sections - if (search_sections) { - selected_values = JSON.parse(search_sections) - } - - if(SHOW_DEBUG===true) { - console.log("Fallback apply. Get hierarchy_sections from wrapper:", selected_values); - } + // from selector if exists + const source_selector = wrap_div.querySelector(".source_selector") + if (source_selector) { + selected_values = [source_selector.value] + } + + // fallback to dataset + if (selected_values.length<1) { + // Get from wrapper dataset + const search_sections = wrap_div.dataset.search_sections; + if (search_sections) { + selected_values = JSON.parse(search_sections) + } + } + + // debug + if(SHOW_DEBUG===true) { + console.log("Fallback apply. Get hierarchy_sections from wrapper:", selected_values); + } } + return selected_values }//end get_search_sections @@ -965,13 +976,13 @@ var service_autocomplete = new function() { const filter_fields = wrap_div.querySelectorAll("input.filter_fields_input") const filter_fields_len = filter_fields.length - if (filter_fields_len<1) { + if (filter_fields_len<1) { return false } - + // Propagate filter_field if (q!=="manual_trigger") { - + // Reset filter fields for (let j = 0; j < filter_fields_len; j++) { filter_fields[j].value = '' @@ -984,30 +995,30 @@ var service_autocomplete = new function() { // PROPAGATE TO FILTER FIELDS for (let j = 0; j < filter_fields_len; j++) { if (ar_q[j]) { - //let q = ar_q[j] + //let q = ar_q[j] let q_type = 'all'//self.determine_q_type(q) - self.propagate_to_filter_field(filter_fields[j], ar_q[j], q_type) - } + self.propagate_to_filter_field(filter_fields[j], ar_q[j], q_type) + } } - }else{ + }else{ for (let i = 0; i < ar_q.length; i++) { //let q = ar_q[i] let q_type = self.determine_q_type(q) // PROPAGATE TO FILTER FIELDS for (let j = 0; j < filter_fields_len; j++) { - self.propagate_to_filter_field(filter_fields[j], ar_q[i], q_type) - } + self.propagate_to_filter_field(filter_fields[j], ar_q[i], q_type) + } } - } + } } // filter_fields_data - const filter_fields_data = {} - for (let i = 0; i < filter_fields_len; i++) { + const filter_fields_data = {} + for (let i = 0; i < filter_fields_len; i++) { const current_tipo = filter_fields[i].dataset.tipo filter_fields_data[current_tipo] = filter_fields[i].value } - + return filter_fields_data }//end get_filter_fields_data @@ -1019,22 +1030,22 @@ var service_autocomplete = new function() { * @return string | bool false */ this.get_filter_by_field_list_tipo = function(wrap_div) { - + let filter_by_field_list_tipo = false let filter_by_list = false if (wrap_div.dataset.filter_by_list) { filter_by_list = JSON.parse(wrap_div.dataset.filter_by_list) - } - + } + if (filter_by_list) { const filter_by_list_len = filter_by_list.length for (let i = 0; i < filter_by_list_len; i++) { filter_by_field_list_tipo = filter_by_list[i].component_tipo break; // Only one at now - } + } } - + return filter_by_field_list_tipo };//end get_filter_by_field_list_tipo @@ -1056,7 +1067,7 @@ var service_autocomplete = new function() { const len = ar_inputs.length for (let i = len - 1; i >= 0; i--) { if(ar_inputs[i].checked) { - let current_val = ar_inputs[i].value // + let current_val = ar_inputs[i].value // if (current_val!=='on') { selected_values.push( JSON.parse(current_val) ) } @@ -1076,7 +1087,7 @@ var service_autocomplete = new function() { this.split_q = function(q) { let ar_q = [] - + const regex = /"[^"]+"|'[^']+'|[^|\s]+|[^\s|]+/ug; const str = q let m; @@ -1086,7 +1097,7 @@ var service_autocomplete = new function() { if (m.index === regex.lastIndex) { regex.lastIndex++; } - + // The result can be accessed through the `m`-variable. m.forEach((match, groupIndex) => { //console.log(`Found match, group ${groupIndex}: ${match}`); @@ -1094,10 +1105,10 @@ var service_autocomplete = new function() { }); } - let divisor = false; + let divisor = false; if (q.indexOf('|')!==-1) { divisor = '|' - } + } const result = { ar_q : ar_q, @@ -1122,7 +1133,7 @@ var service_autocomplete = new function() { const regex_code = /^(\W{1,2})?\d+([.,\/-]+[\d\w]*)?(\D)?$/ const regex_date = /^(\W{1,2})?([0-9]{1,12})-?([0-9]{1,2})?-?([0-9]{1,2})?$/ // /^(\W{1,2})?(-?[0-9]{1,12})(-[0-9]{1,2})?(-[0-9]{1,2})?$/ const regex_int = /^(\W{1,2})?\d+$/ - + switch(true) { case (regex_date.exec(str) !== null) : @@ -1131,11 +1142,11 @@ var service_autocomplete = new function() { case (regex_code.exec(str) !== null) : q_type = 'code' - break; + break; case (regex_int.exec(str) !== null) : q_type = 'int' - break; + break; default: q_type = 'string' @@ -1143,7 +1154,7 @@ var service_autocomplete = new function() { if(SHOW_DEBUG===true) { console.log("[determine_q_type] q_type for "+q+" : ",q_type); } - + return q_type };//end determine_q_type @@ -1152,13 +1163,13 @@ var service_autocomplete = new function() { /** * PROPAGATE_TO_FILTER_FIELD - * @return + * @return */ this.propagate_to_filter_field = function(input_obj, q, q_type) { const ar_type_map = JSON.parse(input_obj.dataset.type_map) const len = ar_type_map.length - + // Force skip types on q_type = 'all' // console.log("len ",len, " - q_type: ",q_type); if (len>0 && q_type!=='all') { @@ -1190,11 +1201,11 @@ var service_autocomplete = new function() { * @return array result */ this.convert_data_to_label_value = function(data) { - + const result = [] const len = data.length for (let i = 0; i < len; i++) { - + const obj = { label : data[i].label, // Label with additional info (parents, model, etc.) value : data[i].key, // Locator stringnified @@ -1206,7 +1217,7 @@ var service_autocomplete = new function() { // Sort result by original property result.sort(function(a,b) {return (a.label.toLowerCase() > b.label.toLowerCase()) ? 1 : ((b.label.toLowerCase() > a.label.toLowerCase()) ? -1 : 0);} ); - + return result };//end convert_data_to_label_value @@ -1218,7 +1229,7 @@ var service_autocomplete = new function() { */ this.toggle_options = function(button_obj) { - const tipo = button_obj.dataset.tipo + const tipo = button_obj.dataset.tipo const container = button_obj.parentNode.parentNode.querySelector('.autosearch_options[data-tipo="'+tipo+'"]') if (container.classList.contains("hide")) { @@ -1239,7 +1250,7 @@ var service_autocomplete = new function() { if (options_div.style.display==="block") { options_div.style.display = ""; }else{ - options_div.style.display = "block"; + options_div.style.display = "block"; } */ @@ -1257,21 +1268,21 @@ var service_autocomplete = new function() { let ar_values = readCookie(cookie_name) if (!ar_values) { return false; - } + } ar_values = JSON.parse(ar_values) function compare_object(obj1, obj2) { - return JSON.stringify(obj1) === JSON.stringify(obj2) + return JSON.stringify(obj1) === JSON.stringify(obj2) } - - const ar_inputs = list_element.querySelectorAll('input') + + const ar_inputs = list_element.querySelectorAll('input') const len = ar_inputs.length for (let i = len - 1; i >= 0; i--) { - - let current_value = ar_inputs[i].value + + let current_value = ar_inputs[i].value if(common.is_json(current_value)===true) { current_value = JSON.parse(current_value) - } + } let checked = false for (var j = ar_values.length - 1; j >= 0; j--) { @@ -1296,10 +1307,10 @@ var service_autocomplete = new function() { /** * BUILD_SEARCH_SECTIONS_LIST - * @return + * @return */ this.build_search_sections_list = function(options) { - + const self = this const tipo = options.tipo @@ -1311,7 +1322,7 @@ var service_autocomplete = new function() { if (!options_wrapper) { console.error("[build_search_sections_list] Error on locate dom element:", target_id); return false - } + } const js_promise = new Promise(function(resolve, reject) { @@ -1324,14 +1335,14 @@ var service_autocomplete = new function() { // li check all const li_all = common.create_dom_element({ - element_type : "li", + element_type : "li", parent : ul }) const checkbox_all = common.create_dom_element({ element_type : "input", type : "checkbox", value : "all", - id : "select_all_" + tipo, + id : "select_all_" + tipo, parent : li_all }) checkbox_all.addEventListener("change",function(){ @@ -1351,23 +1362,23 @@ var service_autocomplete = new function() { // li sections const ar_elements_len = ar_elements.length; for (var i = 0; i < ar_elements_len; i++) { - + const element = ar_elements[i] - + const current_id = "sections_list_" + element.key + "_" + i const current_value = element.key const current_label = element.label - const current_checked = false + const current_checked = false const li = common.create_dom_element({ - element_type : "li", + element_type : "li", parent : ul }) let checkbox = common.create_dom_element({ element_type : "input", type : "checkbox", value : current_value, - id : current_id, + id : current_id, parent : li }) checkbox.setAttribute("checked", current_checked) @@ -1385,8 +1396,8 @@ var service_autocomplete = new function() { resolve(ul) }).then(function(){ - // set_list_element_from_cookie . Read from cookie if exists and update ul list - const list_element = options_wrapper.querySelector('ul.search_sections_list') + // set_list_element_from_cookie . Read from cookie if exists and update ul list + const list_element = options_wrapper.querySelector('ul.search_sections_list') self.set_list_element_from_cookie(list_element, cookie_name) }) @@ -1427,11 +1438,11 @@ var service_autocomplete = new function() { const wrap_div = find_ancestor(input_obj, 'wrap_component') const toponymy_list_ul = wrap_div.querySelector('ul.search_sections_list') const ar_inputs = toponymy_list_ul.querySelectorAll('input') - - const len = ar_inputs.length + + const len = ar_inputs.length for (let i = len - 1; i >= 0; i--) { ar_inputs[i].checked = input_obj.checked - } + } this.save_search_sections(input_obj, cookie_name) @@ -1449,10 +1460,10 @@ var service_autocomplete = new function() { const wrap_div = input.parentNode.parentNode.parentNode.parentNode; const selected_values = service_autocomplete.get_search_sections(wrap_div) const hierarchy_sections = JSON.stringify(selected_values) - + // Store values as cookie to preserve config createCookie(cookie_name, hierarchy_sections, 365) - + return true }//end save_search_sections @@ -1468,7 +1479,7 @@ var service_autocomplete = new function() { * @return promise */ this.build_filter_list = function(options) { - + const self = this const tipo = options.tipo @@ -1481,7 +1492,7 @@ var service_autocomplete = new function() { console.error("[build_filter_list] Error on locate dom element:", target_id); return false } - + const js_promise = new Promise(function(resolve, reject) { @@ -1507,13 +1518,13 @@ var service_autocomplete = new function() { // li check all const li_all = common.create_dom_element({ - element_type : "li", + element_type : "li", parent : ul }) const checkbox_all = common.create_dom_element({ element_type : "input", type : "checkbox", - id : "select_all_" + tipo, + id : "select_all_" + tipo, parent : li_all }) checkbox_all.addEventListener("change",function(){ @@ -1533,23 +1544,23 @@ var service_autocomplete = new function() { // li sections const ar_elements_len = ar_elements.length for (var i = 0; i < ar_elements_len; i++) { - + const element = ar_elements[i] - + const current_id = element.id const current_value = JSON.stringify(element.value) const current_label = element.label - const current_checked = false + const current_checked = false const li = common.create_dom_element({ - element_type : "li", + element_type : "li", parent : ul }) const checkbox = common.create_dom_element({ element_type : "input", type : "checkbox", value : current_value, - id : current_id, + id : current_id, parent : li }) checkbox.setAttribute("checked", current_checked) @@ -1567,7 +1578,7 @@ var service_autocomplete = new function() { resolve(ul) }).then(function(){ - // set_filter_elements . Read from cookie if exists and update ul list + // set_filter_elements . Read from cookie if exists and update ul list const list_element = options_wrapper.querySelector('ul.filter_by_list') self.set_list_element_from_cookie(list_element, cookie_name) }) @@ -1608,7 +1619,7 @@ var service_autocomplete = new function() { this.save_filter_list = function(input_obj, cookie_name) { const self = this - + const wrap_div = component_common.get_wrapper_from_element(input_obj) // input_obj.parentNode.parentNode.parentNode.parentNode const ar_filter_elements = self.get_filter_elements(wrap_div) const filter_elements = JSON.stringify(ar_filter_elements) @@ -1621,7 +1632,7 @@ var service_autocomplete = new function() { if (typeof autocomplete_instance!=="undefined") { // Trigger autocomplete $(autocomplete_search_field).autocomplete( "search", "manual_trigger" ); - } + } } const cookie_create = createCookie(cookie_name, filter_elements, 365); @@ -1638,7 +1649,7 @@ var service_autocomplete = new function() { this.get_filter_elements = function(wrap_div) { const self = this - + let selected_values = false const component_name = wrap_div.dataset.component_name @@ -1655,7 +1666,7 @@ var service_autocomplete = new function() { const len = ar_inputs.length for (let i = len - 1; i >= 0; i--) { if(ar_inputs[i].checked) { - let current_val = ar_inputs[i].value // + let current_val = ar_inputs[i].value // if (current_val!=='on') { selected_values.push( JSON.parse(current_val) ) } @@ -1664,7 +1675,7 @@ var service_autocomplete = new function() { } break; - case "component_autocomplete_hi": + case "component_autocomplete_hi": default: selected_values = self.get_search_sections(wrap_div) break; @@ -1672,7 +1683,7 @@ var service_autocomplete = new function() { return selected_values; };//end get_filter_elements - + /* FILTER_FIELDS INTERFACE ////////////////////////////////////////////////////////////////////// @@ -1710,7 +1721,7 @@ var service_autocomplete = new function() { const div_filter_fields = options_wrapper.querySelector(".filter_fields") if (div_filter_fields) { - //remove the childrens of the dilter_fields div + //remove the childrens of the dilter_fields div /*while (div_filter_fields.firstChild) { div_filter_fields.removeChild(div_filter_fields.firstChild); } @@ -1721,11 +1732,11 @@ var service_autocomplete = new function() { //get the current component_tipo from selection const ar_elements = ar_elements_unfilter.filter(section => section.section_tipo===selection) - + if (!options_wrapper) { console.error("[build_filter_list] Error on locate dom element:", target_id); return false - } + } const js_promise = new Promise(function(resolve, reject) { @@ -1740,27 +1751,27 @@ var service_autocomplete = new function() { if (sections_without_fields.indexOf(selection)!==-1) { //div_container.classList.add("hide") return resolve(div_container) - } + } // ul container const ul = common.create_dom_element({ element_type : "ul", parent : div_container - }) + }) // li sections const ar_elements_len = ar_elements.length for (var i = 0; i < ar_elements_len; i++) { - - const element = ar_elements[i] - + + const element = ar_elements[i] + const current_tipo = element.tipo const current_name = element.name const current_modelo_name = element.modelo_name - const current_type_map = JSON.stringify(element.type_map) + const current_type_map = JSON.stringify(element.type_map) const li = common.create_dom_element({ - element_type : "li", + element_type : "li", parent : ul }) const label = common.create_dom_element({ @@ -1773,7 +1784,7 @@ var service_autocomplete = new function() { element_type : "input", type : "text", name : "filter_field_" + current_tipo, - class_name : "filter_fields_input", + class_name : "filter_fields_input", data_set : { tipo : current_tipo, modelo : current_modelo_name, @@ -1785,7 +1796,7 @@ var service_autocomplete = new function() { input.setAttribute("placeholder", current_tipo) input.addEventListener("keyup",function(){ self.search_from_filter_fields(this) - },false) + },false) } // operator selector @@ -1885,7 +1896,7 @@ var service_autocomplete = new function() { */ this.search_from_filter_fields_busy = false this.search_from_filter_fields = function(input_obj) { - + const self = this // Input model @@ -1898,16 +1909,16 @@ var service_autocomplete = new function() { const wrap_div_id = wrap_div.id const autocomplete_wrapper_id = wrap_div_id.replace('wrapper_', 'aw_') const autocomplete_wrapper = document.getElementById(autocomplete_wrapper_id) - + //if (self.search_from_filter_fields_busy===false) { const autocomplete_search_field = autocomplete_wrapper.querySelector("input.autocomplete_input") self.search_from_filter_fields_busy = true setTimeout(function() { - + // Trigger autocomplete manually - $(autocomplete_search_field).autocomplete( "search", "manual_trigger" ); - + $(autocomplete_search_field).autocomplete( "search", "manual_trigger" ); + //self.search_from_filter_fields_busy = false }, 200) //} @@ -1919,16 +1930,16 @@ var service_autocomplete = new function() { /** * BUILD_SOURCE_SEARCH_SELECTOR - * @return + * @return */ this.build_source_search_selector = function(options) { const self = this - - // debug + + // debug if(SHOW_DEBUG===true) { console.log("[build_source_search_selector] options:",options); - } + } // container const container = document.getElementById(options.target_id) @@ -1949,7 +1960,7 @@ var service_autocomplete = new function() { parent : container, text_content : get_label["origen"] || "Source" }) - + const select = common.create_dom_element({ id : 'select_'+options.target_id, element_type : "select", @@ -1957,15 +1968,15 @@ var service_autocomplete = new function() { class_name : "source_selector", data_set : { source : JSON.stringify(options) - } + } }) // others const ar_search_length = ar_search.length for (let i = 0; i < ar_search_length; i++) { - + const item = ar_search[i] - + const swicher_source = common.create_dom_element({ element_type : "option", parent : select, @@ -1986,10 +1997,11 @@ var service_autocomplete = new function() { // set default value //select.value = "rsc205" self.build_filter_fields(select.value, options) - + + return true };//end build_source_search_selector -}//end class \ No newline at end of file +}//end class diff --git a/lib/dedalo/tools/tool_sort/css/tool_sort.css b/lib/dedalo/tools/tool_sort/css/tool_sort.css index a75d97c7ae..ae44931851 100644 --- a/lib/dedalo/tools/tool_sort/css/tool_sort.css +++ b/lib/dedalo/tools/tool_sort/css/tool_sort.css @@ -1 +1,348 @@ -body{display:flex;flex-direction:column}.html_page_wrap_tool_sort,.html_page_wrap_tool_sort .content_html{height:100%}.tool_sort_icon{background-color:#fd7f26;background-image:url(../../../themes/default/icon_transcription_hover.svg);background-size:18px!important;background-position-y:3px!important;background-position-x:6px!important}.html_page_wrap_tool_sort .content_data{width:100%}.header_tool .select_fast_lang_switch{float:left;position:relative;top:3px;left:10px;text-transform:capitalize;padding-right:20px}.header_tool .lang_selector_in_header .select_fast_lang_switch{opacity:1;margin-left:5px;margin-right:10px;color:#000;max-width:84px}.header_tool .lang_selector_in_header{padding:2px;padding-right:10px;padding-left:10px}.header_tool #header_info{display:block;float:left;position:relative;margin-left:15px;top:8px;line-height:1em}.wrap_tool_sort_page{margin:0;padding:0;display:block;position:absolute;width:99.9%;height:calc(100% - 30px)}#inspector_log.tool_sort_inspector_log{display:block;padding-left:4px;padding-right:4px;z-index:10;min-width:400px;margin-right:72px;margin-left:151px;width:auto;height:2em;position:absolute;margin-top:.7em}#tags_inspector{background-color:#99C9F5;height:24px;width:100%}.view_tool_sort_item{text-align:left}.html_page_wrap_tool_sort .toponymy_list li{width:100%}#section_paginator{padding-top:.5em}.tool_grid{display:grid;grid-template-columns:1fr 1fr;height:calc(100vh - 30px)}.tool_grid>*{padding:2em}.tool_grid .tool_grid_left{background-color:#f3f3f3;overflow:scroll}.tool_grid .tool_grid_left>*{border:none!important}.tool_grid .tool_grid_left #search2_container_selection .search_component{flex-basis:96%}.tool_grid .tool_grid_rigth{background-color:#E8E8E8;overflow:scroll}.tool_grid .tool_grid_rigth>.wrap_component{position:sticky;position:-webkit-sticky;top:0}.tool_grid .tool_grid_rigth .portal_section>.rows_wrapper>.table_row{display:flex}.tool_grid .tool_grid_rigth .portal_section>.rows_wrapper>.table_row .column.component_image,.tool_grid .tool_grid_rigth .portal_section>.rows_wrapper>.table_row .column:last-of-type{max-width:unset!important;width:60%!important;background-color:#fff}.tool_grid .tool_grid_rigth .portal_section>.rows_wrapper>.table_row .column.component_portal>div{display:flex;flex-wrap:wrap}.tool_grid .tool_grid_rigth .portal_section>.rows_wrapper>.table_row>:last-child{display:flex;max-width:unset}.tool_grid .tool_grid_rigth .portal_section>.rows_wrapper>.table_row>:last-child .portal_item_numisdata9{display:flex;max-width:unset;outline:1px solid #eee}.rows_container{overflow:unset!important}.rows_container>.row_container{width:100%;position:relative;align-items:center;background-color:#fff;padding-right:60px;padding-top:1px;padding-bottom:1px}.rows_container>.row_container.snap{position:sticky!important;position:-webkit-sticky!important;top:0;bottom:0;z-index:9;outline:1px solid #ccc;background-color:rgba(249,249,249,.9)}.rows_container>.row_container>.column_cell{flex-direction:column}.rows_container>.row_container>.column_cell>.component_portal_list>.component_portal_column{padding-left:0;width:100%;overflow:hidden}.rows_container>.row_container>.column_cell>.component_portal_list>.component_portal_column>img{height:unset;max-height:150px;cursor:zoom-in;object-fit:contain}.rows_container>.row_container>.column_cell>.component_input_text_list{padding:1em;text-align:center;width:100%;font-size:18px}.rows_container>.row_container .drag_item{background-image:url(../../../themes/default/move.svg);background-repeat:no-repeat;background-position:center;background-size:18px 18px;opacity:.3;width:5.8%;height:100%;position:absolute;right:-1px;bottom:0;cursor:move}.rows_container>.row_container .drag_item:hover:not(.row_used){background-image:url(../../../themes/default/move_hover.svg);background-color:#9B9B9B;opacity:.7}#search_group_container{flex-wrap:wrap}#search_group_container #root_search_group{width:100%}#search_group_container .max_group{margin-left:5px;width:32%}#search_group_container .reset_group{width:32%}#search_group_container #button_submit{width:31.5%;margin-right:5px}#sections_select{height:25px;float:right;margin:5px;height:2.3em}.button_open_search2{margin-left:2px}#thesaurus_container{padding:10px;position:-webkit-sticky;position:sticky;top:0}.css_wrap_portal>.content_data{overflow:unset}.tool_grid_left .css_wrap_portal>.content_data>.css_button_generic{display:none}.css_wrap_portal>.content_data .mosaic_ul .mosaic_li .button_delete,.css_wrap_portal>.content_data .mosaic_ul .mosaic_li .button_edit{display:none!important}.css_wrap_portal .mosaic_item .div_image_portal_list_view_mosaic{background-size:contain!important;background-color:#fff}.wrap_ts_object .data_container .wrap_component{border:none!important}.row_used{transition:all 1s ease;background-color:#ff9b00;opacity:.8!important}.dragover{outline:2px dashed orange}.drop{animation-duration:1s;animation-name:outline_to_green}@keyframes outline_to_green{from{outline:2px dashed green}to{outline:2px dashed transparent}}#section_filter_container{display:none}.row_container img{-webkit-user-select:none;-webkit-user-drag:none;-webkit-app-region:no-drag;cursor:default}.column_original_duplicate{padding:1em;height:100%}.column_original_duplicate>*{padding:.2em}.custom_order_button{padding:.5em}.custom_order_button.active{background-color:#79E47F}.button_original_duplicate{padding:.5em;margin-left:1em}.row_snap{height:100%;padding:1em;border-left:1px solid rgba(238,238,238,.43921569)}.row_snap>*{margin-left:.2em;padding:.2em}.rows_container>.row_container{grid-template-columns:50px 50px 1fr 1fr 270px 270px 85px 75px}/*# sourceMappingURL=tool_sort.css.map */ +/* SIZES +----------------------------------------- */ +/* COLORS +----------------------------------------- */ +body { + display: flex; + flex-direction: column; +} +.html_page_wrap_tool_sort, +.html_page_wrap_tool_sort .content_html { + height: 100%; +} +.tool_sort_icon { + background-color: #fd7f26; + background-image: url(../../../themes/default/icon_transcription_hover.svg); + background-size: 18px !important; + background-position-y: 3px !important; + background-position-x: 6px !important; +} +/* tool_sort CASE + --------------------------------------------- */ +.html_page_wrap_tool_sort .content_data { + width: 100%; +} +/* HEADER +------------------------------------------------------ */ +.header_tool .select_fast_lang_switch { + float: left; + position: relative; + top: 3px; + left: 10px; + text-transform: capitalize; + padding-right: 20px; +} +.header_tool .lang_selector_in_header .select_fast_lang_switch { + opacity: 1; + margin-left: 5px; + margin-right: 10px; + color: #000000; + max-width: 84px; +} +.header_tool .lang_selector_in_header { + padding: 2px; + padding-right: 10px; + padding-left: 10px; +} +.header_tool #header_info { + display: block; + float: left; + position: relative; + margin-left: 15px; + top: 8px; + line-height: 1em; +} +/* PAGE +------------------------------------------------------ */ +.wrap_tool_sort_page { + margin: 0; + padding: 0; + display: block; + position: absolute; + width: 99.9%; + height: calc(100% - 30px); +} +#inspector_log.tool_sort_inspector_log { + display: block; + padding-left: 4px; + padding-right: 4px; + z-index: 10; + min-width: 400px; + margin-right: 72px; + margin-left: 151px; + width: auto; + height: 2em; + position: absolute; + margin-top: 0.7em; +} +/* TAGS INSPECTOR +------------------------------------------------------ */ +#tags_inspector { + background-color: #99C9F5; + height: 24px; + width: 100%; +} +.view_tool_sort_item { + text-align: left; +} +.html_page_wrap_tool_sort .toponymy_list li { + width: 100%; +} +#section_paginator { + padding-top: 0.5em; +} +.tool_grid { + display: grid; + grid-template-columns: 1fr 1fr; + height: calc(100vh - 30px); +} +.tool_grid > * { + padding: 2em; +} +.tool_grid .tool_grid_left { + background-color: #f3f3f3; + overflow: scroll; +} +.tool_grid .tool_grid_left > * { + border: none !important; +} +.tool_grid .tool_grid_left #search2_container_selection .search_component { + flex-basis: 96%; +} +.tool_grid .tool_grid_rigth { + background-color: #E8E8E8; + overflow: scroll; +} +.tool_grid .tool_grid_rigth > .wrap_component { + position: sticky; + position: -webkit-sticky; + top: 0; +} +.tool_grid .tool_grid_rigth .portal_section > .rows_wrapper > .table_row { + display: flex; +} +.tool_grid .tool_grid_rigth .portal_section > .rows_wrapper > .table_row .column.component_image, +.tool_grid .tool_grid_rigth .portal_section > .rows_wrapper > .table_row .column:last-of-type { + max-width: unset!important; + width: 60%!important; + background-color: #fff; +} +.tool_grid .tool_grid_rigth .portal_section > .rows_wrapper > .table_row .column.component_portal > div { + display: flex; + flex-wrap: wrap; +} +.tool_grid .tool_grid_rigth .portal_section > .rows_wrapper > .table_row > :last-child { + display: flex; + max-width: unset; +} +.tool_grid .tool_grid_rigth .portal_section > .rows_wrapper > .table_row > :last-child .portal_item_numisdata9 { + display: flex; + max-width: unset; + outline: 1px solid #eeeeee; +} +.rows_container { + overflow: unset !important; + min-width: 550px; +} +.rows_container > .row_container { + width: 100%; + position: relative; + align-items: center; + background-color: #ffffff; + padding-right: 40px; + padding-top: 1px; + padding-bottom: 1px; +} +.rows_container > .row_container.snap { + position: sticky !important; + position: -webkit-sticky !important; + top: 0; + bottom: 0; + z-index: 9; + outline: 1px solid #cccccc; + background-color: rgba(249, 249, 249, 0.9); +} +.rows_container > .row_container > div:nth-child(-n+4) { + grid-column-start: 1; +} +.rows_container > .row_container > div:nth-child(n+5) { + grid-row-start: 1; + grid-row-end: 5; +} +.rows_container > .row_container > div:nth-child(5) { + grid-column-start: 2; +} +.rows_container > .row_container > div:nth-child(6) { + grid-column-start: 3; +} +.rows_container > .row_container > div:nth-child(7) { + grid-column-start: 4; + grid-row-end: 3; +} +.rows_container > .row_container > div:nth-child(8) { + grid-column-start: 4; + grid-row-start: 3; +} +.rows_container > .row_container > .column_cell { + flex-direction: column; +} +.rows_container > .row_container > .column_cell > .component_portal_list > .component_portal_column { + padding-left: 0; + width: 100%; + overflow: hidden; +} +.rows_container > .row_container > .column_cell > .component_portal_list > .component_portal_column > img { + height: unset; + max-height: 150px; + cursor: zoom-in; + object-fit: contain; +} +.rows_container > .row_container > .column_cell > .component_input_text_list { + padding: 1em; + text-align: center; + width: 100%; + font-size: 18px; +} +.rows_container > .row_container .drag_item { + background-image: url(../../../themes/default/move.svg); + background-repeat: no-repeat; + background-position: center; + background-size: 18px 18px; + opacity: 0.3; + width: 5.8%; + height: 100%; + position: absolute; + right: -1px; + bottom: 0; + cursor: move; +} +.rows_container > .row_container .drag_item:hover:not(.row_used) { + background-image: url(../../../themes/default/move_hover.svg); + background-color: #9B9B9B; + opacity: 0.7; +} +#search_group_container { + flex-wrap: wrap; +} +#search_group_container #root_search_group { + width: 100%; +} +#search_group_container .max_group { + margin-left: 5px; + width: 32%; +} +#search_group_container .reset_group { + width: 32%; +} +#search_group_container #button_submit { + width: 31.5%; + margin-right: 5px; +} +#sections_select { + height: 25px; + float: right; + margin: 5px; + height: 2.3em; +} +.button_open_search2 { + margin-left: 2px; +} +#thesaurus_container { + padding: 10px; + position: -webkit-sticky; + position: sticky; + top: 0; +} +.css_wrap_portal > .content_data { + overflow: unset; +} +.tool_grid_left .css_wrap_portal > .content_data > .css_button_generic { + display: none; +} +.css_wrap_portal > .content_data .mosaic_ul .mosaic_li .button_edit, +.css_wrap_portal > .content_data .mosaic_ul .mosaic_li .button_delete { + display: none !important; +} +.css_wrap_portal .mosaic_item .div_image_portal_list_view_mosaic { + background-size: contain !important; + background-color: #ffffff; +} +.wrap_ts_object .data_container .wrap_component { + border: none !important; +} +.row_used { + transition: all 1s ease; + background-color: #ff9b00; + opacity: 0.8 !important; + /* + &:after { + content: " "; + background-color: #ec8a0a; + color: #fff; + position: absolute; + right: 0; + top: 0; + width: 29px; + text-align: center; + height: 2em; + line-height: 2em; + }*/ +} +.dragover { + outline: 2px dashed orange; +} +.drop { + animation-duration: 1s; + animation-name: outline_to_green; +} +@keyframes outline_to_green { + from { + outline: 2px dashed green; + } + to { + outline: 2px dashed transparent; + } +} +#section_filter_container { + display: none; +} +.row_container img { + -webkit-user-select: none; + -webkit-user-drag: none; + -webkit-app-region: no-drag; + cursor: default; +} +.column_original_duplicate { + /* padding-top: .5em; */ + /* padding-bottom: .5em; */ + /* border-left: 1px solid #eee; */ + padding: 1em; + height: 100%; +} +.column_original_duplicate > * { + padding: 0.2em; +} +.custom_order_button { + padding: 0.5em; +} +.custom_order_button.active { + background-color: #79E47F; +} +.button_original_duplicate { + padding: 0.5em; + margin-left: 1em; +} +.row_snap { + height: 100%; + padding: 1em; + border-left: 1px solid rgba(238, 238, 238, 0.43921569); +} +.row_snap > * { + margin-left: 0.2em; + padding: 0.2em; +} +.rows_container > .row_container { + grid-template-columns: minmax(75px, 1fr) 160px 160px 85px; + grid-template-rows: 1fr 1fr 1fr 1fr; +} +/*# sourceMappingURL=tool_sort.css.map */ \ No newline at end of file diff --git a/lib/dedalo/tools/tool_sort/css/tool_sort.css.map b/lib/dedalo/tools/tool_sort/css/tool_sort.css.map index e8c96bc732..087a71d966 100644 --- a/lib/dedalo/tools/tool_sort/css/tool_sort.css.map +++ b/lib/dedalo/tools/tool_sort/css/tool_sort.css.map @@ -1 +1 @@ -{"version":3,"sources":["tool_sort.less"],"names":[],"mappings":"AAUA,KACE,QAAA,KACE,eAAA,OAGJ,0BAAqD,wCACpD,OAAA,KAID,gBACC,iBAAA,QACA,iBAAA,0DAEA,gBAAA,eACG,sBAAA,cACA,sBAAA,cAMuB,wCACzB,MAAA,KASa,sCACZ,MAAA,KACA,SAAA,SACA,IAAA,IACA,KAAA,KACA,eAAA,WACA,cAAA,KAGqC,+DACrC,QAAA,EACA,YAAA,IACA,aAAA,KACA,MAAA,KACA,UAAA,KAEY,sCACZ,QAAA,IACA,cAAA,KACA,aAAA,KAEY,0BACZ,QAAA,MACA,MAAA,KACA,SAAA,SACA,YAAA,KACA,IAAA,IACA,YAAA,IAOF,qBAEC,OAAA,EACA,QAAA,EACA,QAAA,MACA,SAAA,SAMA,MAAA,MACA,OAAA,kBAMa,uCAEb,QAAA,MACA,aAAA,IACA,cAAA,IACA,QAAA,GACA,UAAA,MACA,aAAA,KACA,YAAA,MACA,MAAA,KACA,OAAA,IACA,SAAA,SACA,WAAA,KAOD,gBACC,iBAAA,QACA,OAAA,KACA,MAAA,KAID,qBACC,WAAA,KAKwC,4CACxC,MAAA,KAOD,mBACC,YAAA,KAMD,WACC,QAAA,KAEA,sBAAA,IAAA,IACA,OAAQ,mBAER,aACC,QAAA,IAID,2BACC,iBAAA,QACA,SAAA,OAEA,6BACC,OAAA,eAG4B,0EAC5B,WAAA,IAMF,4BACC,iBAAA,QACA,SAAA,OAEA,4CACC,SAAA,OACA,SAAA,eACA,IAAA,EAegB,qEACd,QAAA,KAGU,6FAAyB,0FAClC,UAAA,gBACA,MAAA,cACA,iBAAA,KAIG,kGACC,QAAA,KACA,UAAA,KAIF,iFAEC,QAAA,KACA,UAAA,MAEA,yGAEC,QAAA,KACA,UAAA,MAEA,QAAA,IAAA,MAAA,KA6BV,gBACC,SAAA,gBAEA,+BAIC,MAAA,KACA,SAAA,SACA,YAAA,OACA,iBAAA,KACA,cAAA,KACA,YAAA,IACA,eAAA,IAEC,oCACA,SAAA,iBACA,SAAA,yBACA,IAAA,EACA,OAAA,EACA,QAAA,EACA,QAAA,IAAA,MAAA,KAEA,iBAAA,qBAGD,4CAEC,eAAA,OAUyB,4FACvB,aAAA,EAEA,MAAA,KACA,SAAA,OAGA,gGAEC,OAAA,MACA,WAAA,MACA,OAAA,QACA,WAAA,QAKH,uEACC,QAAA,IACA,WAAA,OACA,MAAA,KACA,UAAA,KAKF,0CACC,iBAAA,sCACA,kBAAA,UACA,oBAAA,OACA,gBAAA,KAAA,KAEA,QAAA,GACA,MAAA,KACA,OAAA,KACA,SAAA,SACA,MAAA,KACA,OAAA,EACA,OAAA,KAEW,+DACV,iBAAA,4CACA,iBAAA,QACA,QAAA,GAWJ,wBACC,UAAA,KAEA,2CACC,MAAA,KAID,mCACC,YAAA,IACA,MAAA,IAED,qCACC,MAAA,IAED,uCACC,MAAA,MACA,aAAA,IAOF,iBACC,OAAA,KACA,MAAA,MACA,OAAA,IACA,OAAA,MAMD,qBACC,YAAA,IAKD,qBACC,QAAA,KAEA,SAAA,eACA,SAAA,OACA,IAAA,EAMA,+BAEC,SAAA,MAEA,mEAGE,QAAA,KAIwD,oEAApC,kEACrB,QAAA,eAIW,iEACZ,gBAAA,kBACA,iBAAA,KAK8B,gDAC/B,OAAA,eAKD,UACC,WAAA,IAAA,GAAA,KAEA,iBAAA,QACG,QAAA,aAmBJ,UACC,QAAA,IAAA,OAAA,OAED,MAEC,mBAAA,GACA,eAAA,iBAED,4BACE,KACE,QAAA,IAAA,OAAA,MAEF,GACE,QAAA,IAAA,OAAA,aAKL,0BACC,QAAA,KAGc,mBACd,oBAAA,KACA,kBAAA,KACA,mBAAA,QACA,OAAA,QAKA,2BAMC,QAAA,IACA,OAAA,KAEA,6BACC,QAAA,KAUF,qBAEC,QAAA,KAEC,4BACA,iBAAA,QAKF,2BAGC,QAAA,KACA,YAAA,IAMD,UACC,OAAA,KACA,QAAA,IAEA,YAAA,IAAA,MAAA,4BAEA,YACC,YAAA,KACG,QAAA,KAUU,+BAEX,sBAAA,KAAA,KAAA,IAAA,IAAA,MAAA,MAAA,KAAA"} +{"version":3,"sources":["tool_sort.less"],"names":[],"mappings":";;;;AAUA;EACE,aAAA;EACE,sBAAA;;AAGJ;AAA2B,yBAA0B;EACpD,YAAA;;AAID;EACC,yBAAA;EACA,2EAAA;EAEA,gCAAA;EACG,qCAAA;EACA,qCAAA;;;;AAMH,yBAA0B;EACzB,WAAA;;;;AASA,YAAa;EACZ,WAAA;EACA,kBAAA;EACA,QAAA;EACA,UAAA;EACA,0BAAA;EACA,mBAAA;;AAGD,YAAa,yBAAyB;EACrC,UAAA;EACA,gBAAA;EACA,kBAAA;EACA,cAAA;EACA,eAAA;;AAED,YAAa;EACZ,YAAA;EACA,mBAAA;EACA,kBAAA;;AAED,YAAa;EACZ,cAAA;EACA,WAAA;EACA,kBAAA;EACA,iBAAA;EACA,QAAA;EACA,gBAAA;;;;AAOF;EAEC,SAAA;EACA,UAAA;EACA,cAAA;EACA,kBAAA;EAMA,YAAA;EACA,yBAAA;;AAMD,cAAc;EAEb,cAAA;EACA,iBAAA;EACA,kBAAA;EACA,WAAA;EACA,gBAAA;EACA,kBAAA;EACA,kBAAA;EACA,WAAA;EACA,WAAA;EACA,kBAAA;EACA,iBAAA;;;;AAOD;EACC,yBAAA;EACA,YAAA;EACA,WAAA;;AAID;EACC,gBAAA;;AAKD,yBAA0B,eAAe;EACxC,WAAA;;AAOD;EACC,kBAAA;;AAMD;EACC,aAAA;EAEA,8BAAA;EACA,QAAQ,kBAAR;;AAJD,UAMC;EACC,YAAA;;AAPF,UAWC;EACC,yBAAA;EACA,gBAAA;;AAbF,UAWC,gBAIC;EACC,uBAAA;;AAhBH,UAWC,gBAQC,6BAA6B;EAC5B,eAAA;;AApBH,UA0BC;EACC,yBAAA;EACA,gBAAA;;AA5BF,UA0BC,iBAIC;EACC,gBAAA;EACA,wBAAA;EACA,MAAA;;AAjCH,UA0BC,iBAWE,gBAWC,gBAAe;EACd,aAAA;;AAjDL,UA0BC,iBAWE,gBAWC,gBAAe,aAIX,QAAO;AApDf,UA0BC,iBAWE,gBAWC,gBAAe,aAIc,QAAO;EAClC,0BAAA;EACA,oBAAA;EACA,sBAAA;;AAvDN,UA0BC,iBAWE,gBAWC,gBAAe,aAUX,QAAO,iBACN;EACC,aAAA;EACA,eAAA;;AA7DV,UA0BC,iBAWE,gBAWC,gBAAe,aAiBX;EAEC,aAAA;EACA,gBAAA;;AApET,UA0BC,iBAWE,gBAWC,gBAAe,aAiBX,cAKC;EAEC,aAAA;EACA,gBAAA;EAEA,0BAAA;;AA6BV;EACC,0BAAA;EACA,gBAAA;;AAFD,eAIC;EAIC,WAAA;EACA,kBAAA;EACA,mBAAA;EACA,yBAAA;EACA,mBAAA;EACA,gBAAA;EACA,mBAAA;;AAEA,eAZD,iBAYE;EACA,2BAAA;EACA,wBAAA;EACA,MAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EAEA,0CAAA;;AAxBH,eAIC,iBAsBC,MAAI,UAAU;EACb,oBAAA;;AA3BH,eAIC,iBAyBC,MAAI,UAAU;EACb,iBAAA;EACA,eAAA;;AA/BH,eAIC,iBA6BC,MAAI,UAAU;EACb,oBAAA;;AAlCH,eAIC,iBAgCC,MAAI,UAAU;EACb,oBAAA;;AArCH,eAIC,iBAmCC,MAAI,UAAU;EACb,oBAAA;EACA,eAAA;;AAzCH,eAIC,iBAuCC,MAAI,UAAU;EACb,oBAAA;EACA,iBAAA;;AA7CH,eAIC,iBA2CC;EAEC,sBAAA;;AAjDH,eAIC,iBA2CC,eAYE,yBAAwB;EACvB,eAAA;EAEA,WAAA;EACA,gBAAA;;AA/DL,eAIC,iBA2CC,eAYE,yBAAwB,2BAOvB;EAEC,aAAA;EACA,iBAAA;EACA,eAAA;EACA,mBAAA;;AAvEN,eAIC,iBA2CC,eA6BC;EACC,YAAA;EACA,kBAAA;EACA,WAAA;EACA,eAAA;;AAhFJ,eAIC,iBAiFC;EACC,uDAAA;EACA,4BAAA;EACA,2BAAA;EACA,0BAAA;EAEA,YAAA;EACA,WAAA;EACA,YAAA;EACA,kBAAA;EACA,WAAA;EACA,SAAA;EACA,YAAA;;AAEA,eA/FF,iBAiFC,WAcE,MAAM,IAAI;EACV,6DAAA;EACA,yBAAA;EACA,YAAA;;AAWJ;EACC,eAAA;;AADD,uBAGC;EACC,WAAA;;AAJF,uBAQC;EACC,gBAAA;EACA,UAAA;;AAVF,uBAYC;EACC,UAAA;;AAbF,uBAeC;EACC,YAAA;EACA,iBAAA;;AAOF;EACC,YAAA;EACA,YAAA;EACA,WAAA;EACA,aAAA;;AAMD;EACC,gBAAA;;AAKD;EACC,aAAA;EAEA,wBAAA;EACA,gBAAA;EACA,MAAA;;AAID,gBAEC;EAEC,eAAA;;AAIC,eAAgB,iBANlB,gBAIC;EAGE,aAAA;;AATJ,gBAEC,gBAWC,WAAW,WAAW;AAbxB,gBAEC,gBAWqC,WAAW,WAAW;EACzD,wBAAA;;AAdH,gBAkBC,aAAa;EACZ,mCAAA;EACA,yBAAA;;AAKF,eAAgB,gBAAgB;EAC/B,uBAAA;;AAKD;EACC,uBAAA;EAEA,yBAAA;EACG,YAAA;;;;;;;;;;;;;;;AAmBJ;EACC,0BAAA;;AAED;EAEC,sBAAA;EACA,gCAAA;;AAED;EACE;IACE,yBAAA;;EAEF;IACE,+BAAA;;;AAKL;EACC,aAAA;;AAGD,cAAe;EACd,yBAAA;EACA,uBAAA;EACA,2BAAA;EACA,eAAA;;AAKA;;;;EAMC,YAAA;EACA,YAAA;;AAPD,0BASC;EACC,cAAA;;AAUF;EAEC,cAAA;;AAEA,oBAAC;EACA,yBAAA;;AAKF;EAGC,cAAA;EACA,gBAAA;;AAMD;EACC,YAAA;EACA,YAAA;EAEA,sDAAA;;AAJD,SAMC;EACC,kBAAA;EACG,cAAA;;AAUL,eAAe;EAEX,uBAAuB,kCAAvB;EACA,mCAAA","file":"tool_sort.css"} \ No newline at end of file diff --git a/lib/dedalo/tools/tool_sort/css/tool_sort.less b/lib/dedalo/tools/tool_sort/css/tool_sort.less index 32e9692cfd..acac74ee3d 100644 --- a/lib/dedalo/tools/tool_sort/css/tool_sort.less +++ b/lib/dedalo/tools/tool_sort/css/tool_sort.less @@ -245,6 +245,7 @@ body { // rows_container .rows_container { overflow: unset !important; + min-width: 550px; >.row_container { //display: flex; @@ -254,7 +255,7 @@ body { position: relative; align-items: center; background-color: #ffffff; - padding-right: 60px; + padding-right: 40px; padding-top: 1px; padding-bottom: 1px; @@ -268,7 +269,27 @@ body { background-color: rgba(249, 249, 249, 0.90);; // ? } - + >div:nth-child(-n+4){ + grid-column-start: 1; + } + >div:nth-child(n+5){ + grid-row-start: 1; + grid-row-end: 5; + } + >div:nth-child(5){ + grid-column-start: 2; + } + >div:nth-child(6){ + grid-column-start: 3; + } + >div:nth-child(7){ + grid-column-start: 4; + grid-row-end: 3; + } + >div:nth-child(8){ + grid-column-start: 4; + grid-row-start: 3; + } >.column_cell { flex-direction: column; @@ -529,8 +550,8 @@ body { // provisional para numisdata .rows_container>.row_container { //grid-template-columns: 50px 50px 1fr 1fr 150px 150px 95px 75px; - grid-template-columns: 50px 50px 1fr 1fr 270px 270px 85px 75px; - + grid-template-columns: minmax(75px, 1fr) 160px 160px 85px; + grid-template-rows: 1fr 1fr 1fr 1fr; >.column_cell { //height: 100%; //height: 100%; diff --git a/vendor/evenement/evenement/.gitignore b/vendor/evenement/evenement/.gitignore new file mode 100644 index 0000000000..987e2a253c --- /dev/null +++ b/vendor/evenement/evenement/.gitignore @@ -0,0 +1,2 @@ +composer.lock +vendor diff --git a/vendor/evenement/evenement/.travis.yml b/vendor/evenement/evenement/.travis.yml new file mode 100644 index 0000000000..65ba0ced9c --- /dev/null +++ b/vendor/evenement/evenement/.travis.yml @@ -0,0 +1,24 @@ +language: php + +php: + - 7.0 + - 7.1 + - hhvm + - nightly + +matrix: + allow_failures: + - php: hhvm + - php: nightly + +before_script: + - wget http://getcomposer.org/composer.phar + - php composer.phar install + +script: + - ./vendor/bin/phpunit --coverage-text + - php -n examples/benchmark-emit-no-arguments.php + - php -n examples/benchmark-emit-one-argument.php + - php -n examples/benchmark-emit.php + - php -n examples/benchmark-emit-once.php + - php -n examples/benchmark-remove-listener-once.php diff --git a/vendor/evenement/evenement/CHANGELOG.md b/vendor/evenement/evenement/CHANGELOG.md new file mode 100644 index 0000000000..568f2295a5 --- /dev/null +++ b/vendor/evenement/evenement/CHANGELOG.md @@ -0,0 +1,35 @@ +CHANGELOG +========= + + +* v3.0.1 (2017-07-23) + + * Resolved regression introduced in once listeners in v3.0.0 [#49](https://github.com/igorw/evenement/pull/49) + +* v3.0.0 (2017-07-23) + + * Passing null as event name throw exception [#46](https://github.com/igorw/evenement/pull/46), and [#47](https://github.com/igorw/evenement/pull/47) + * Performance improvements [#39](https://github.com/igorw/evenement/pull/39), and [#45](https://github.com/igorw/evenement/pull/45) + * Remove once listeners [#44](https://github.com/igorw/evenement/pull/44), [#45](https://github.com/igorw/evenement/pull/45) + +* v2.1.0 (2017-07-17) + + * Chaining for "on" method [#30](https://github.com/igorw/evenement/pull/30) + * Unit tests (on Travis) improvements [#33](https://github.com/igorw/evenement/pull/33), [#36](https://github.com/igorw/evenement/pull/36), and [#37](https://github.com/igorw/evenement/pull/37) + * Benchmarks added [#35](https://github.com/igorw/evenement/pull/35), and [#40](https://github.com/igorw/evenement/pull/40) + * Minor performance improvements [#42](https://github.com/igorw/evenement/pull/42), and [#38](https://github.com/igorw/evenement/pull/38) + +* v2.0.0 (2012-11-02) + + * Require PHP >=5.4.0 + * Added EventEmitterTrait + * Removed EventEmitter2 + +* v1.1.0 (2017-07-17) + + * Chaining for "on" method [#29](https://github.com/igorw/evenement/pull/29) + * Minor performance improvements [#43](https://github.com/igorw/evenement/pull/43) + +* v1.0.0 (2012-05-30) + + * Inital stable release diff --git a/vendor/evenement/evenement/LICENSE b/vendor/evenement/evenement/LICENSE new file mode 100644 index 0000000000..d9a37d0a04 --- /dev/null +++ b/vendor/evenement/evenement/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Igor Wiedler + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/evenement/evenement/README.md b/vendor/evenement/evenement/README.md new file mode 100644 index 0000000000..94430119b1 --- /dev/null +++ b/vendor/evenement/evenement/README.md @@ -0,0 +1,83 @@ +# Événement + +Événement is a very simple event dispatching library for PHP. + +It has the same design goals as [Silex](http://silex-project.org) and +[Pimple](http://pimple-project.org), to empower the user while staying concise +and simple. + +It is very strongly inspired by the EventEmitter API found in +[node.js](http://nodejs.org). + +[![Build Status](https://secure.travis-ci.org/igorw/evenement.png?branch=master)](http://travis-ci.org/igorw/evenement) + +## Fetch + +The recommended way to install Événement is [through composer](http://getcomposer.org). + +Just create a composer.json file for your project: + +```JSON +{ + "require": { + "evenement/evenement": "^3.0 || ^2.0" + } +} +``` + +**Note:** The `3.x` version of Événement requires PHP 7 and the `2.x` version requires PHP 5.4. If you are +using PHP 5.3, please use the `1.x` version: + +```JSON +{ + "require": { + "evenement/evenement": "^1.0" + } +} +``` + +And run these two commands to install it: + + $ curl -s http://getcomposer.org/installer | php + $ php composer.phar install + +Now you can add the autoloader, and you will have access to the library: + +```php +on('user.created', function (User $user) use ($logger) { + $logger->log(sprintf("User '%s' was created.", $user->getLogin())); +}); +``` + +### Emitting Events + +```php +emit('user.created', [$user]); +``` + +Tests +----- + + $ ./vendor/bin/phpunit + +License +------- +MIT, see LICENSE. diff --git a/vendor/evenement/evenement/composer.json b/vendor/evenement/evenement/composer.json new file mode 100644 index 0000000000..cbb4827b69 --- /dev/null +++ b/vendor/evenement/evenement/composer.json @@ -0,0 +1,29 @@ +{ + "name": "evenement/evenement", + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": ["event-dispatcher", "event-emitter"], + "license": "MIT", + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "autoload": { + "psr-0": { + "Evenement": "src" + } + }, + "autoload-dev": { + "psr-0": { + "Evenement": "tests" + }, + "files": ["tests/Evenement/Tests/functions.php"] + } +} diff --git a/vendor/evenement/evenement/doc/00-intro.md b/vendor/evenement/evenement/doc/00-intro.md new file mode 100644 index 0000000000..6c28a2ab2c --- /dev/null +++ b/vendor/evenement/evenement/doc/00-intro.md @@ -0,0 +1,28 @@ +# Introduction + +Événement is is French and means "event". The événement library aims to +provide a simple way of subscribing to events and notifying those subscribers +whenever an event occurs. + +The API that it exposes is almost a direct port of the EventEmitter API found +in node.js. It also includes an "EventEmitter". There are some minor +differences however. + +The EventEmitter is an implementation of the publish-subscribe pattern, which +is a generalized version of the observer pattern. The observer pattern +specifies an observable subject, which observers can register themselves to. +Once something interesting happens, the subject notifies its observers. + +Pub/sub takes the same idea but encapsulates the observation logic inside a +separate object which manages all of its subscribers or listeners. Subscribers +are bound to an event name, and will only receive notifications of the events +they subscribed to. + +**TLDR: What does evenement do, in short? It provides a mapping from event +names to a list of listener functions and triggers each listener for a given +event when it is emitted.** + +Why do we do this, you ask? To achieve decoupling. + +It allows you to design a system where the core will emit events, and modules +are able to subscribe to these events. And respond to them. diff --git a/vendor/evenement/evenement/doc/01-api.md b/vendor/evenement/evenement/doc/01-api.md new file mode 100644 index 0000000000..17ba333ecc --- /dev/null +++ b/vendor/evenement/evenement/doc/01-api.md @@ -0,0 +1,91 @@ +# API + +The API that événement exposes is defined by the +`Evenement\EventEmitterInterface`. The interface is useful if you want to +define an interface that extends the emitter and implicitly defines certain +events to be emitted, or if you want to type hint an `EventEmitter` to be +passed to a method without coupling to the specific implementation. + +## on($event, callable $listener) + +Allows you to subscribe to an event. + +Example: + +```php +$emitter->on('user.created', function (User $user) use ($logger) { + $logger->log(sprintf("User '%s' was created.", $user->getLogin())); +}); +``` + +Since the listener can be any callable, you could also use an instance method +instead of the anonymous function: + +```php +$loggerSubscriber = new LoggerSubscriber($logger); +$emitter->on('user.created', array($loggerSubscriber, 'onUserCreated')); +``` + +This has the benefit that listener does not even need to know that the emitter +exists. + +You can also accept more than one parameter for the listener: + +```php +$emitter->on('numbers_added', function ($result, $a, $b) {}); +``` + +## once($event, callable $listener) + +Convenience method that adds a listener which is guaranteed to only be called +once. + +Example: + +```php +$conn->once('connected', function () use ($conn, $data) { + $conn->send($data); +}); +``` + +## emit($event, array $arguments = []) + +Emit an event, which will call all listeners. + +Example: + +```php +$conn->emit('data', [$data]); +``` + +The second argument to emit is an array of listener arguments. This is how you +specify more args: + +```php +$result = $a + $b; +$emitter->emit('numbers_added', [$result, $a, $b]); +``` + +## listeners($event) + +Allows you to inspect the listeners attached to an event. Particularly useful +to check if there are any listeners at all. + +Example: + +```php +$e = new \RuntimeException('Everything is broken!'); +if (0 === count($emitter->listeners('error'))) { + throw $e; +} +``` + +## removeListener($event, callable $listener) + +Remove a specific listener for a specific event. + +## removeAllListeners($event = null) + +Remove all listeners for a specific event or all listeners all together. This +is useful for long-running processes, where you want to remove listeners in +order to allow them to get garbage collected. diff --git a/vendor/evenement/evenement/doc/02-plugin-system.md b/vendor/evenement/evenement/doc/02-plugin-system.md new file mode 100644 index 0000000000..6a08371931 --- /dev/null +++ b/vendor/evenement/evenement/doc/02-plugin-system.md @@ -0,0 +1,155 @@ +# Example: Plugin system + +In this example I will show you how to create a generic plugin system with +événement where plugins can alter the behaviour of the app. The app is a blog. +Boring, I know. By using the EventEmitter it will be easy to extend this blog +with additional functionality without modifying the core system. + +The blog is quite basic. Users are able to create blog posts when they log in. +The users are stored in a static config file, so there is no sign up process. +Once logged in they get a "new post" link which gives them a form where they +can create a new blog post with plain HTML. That will store the post in a +document database. The index lists all blog post titles by date descending. +Clicking on the post title will take you to the full post. + +## Plugin structure + +The goal of the plugin system is to allow features to be added to the blog +without modifying any core files of the blog. + +The plugins are managed through a config file, `plugins.json`. This JSON file +contains a JSON-encoded list of class-names for plugin classes. This allows +you to enable and disable plugins in a central location. The initial +`plugins.json` is just an empty array: +```json +[] +``` + +A plugin class must implement the `PluginInterface`: +```php +interface PluginInterface +{ + function attachEvents(EventEmitterInterface $emitter); +} +``` + +The `attachEvents` method allows the plugin to attach any events to the +emitter. For example: +```php +class FooPlugin implements PluginInterface +{ + public function attachEvents(EventEmitterInterface $emitter) + { + $emitter->on('foo', function () { + echo 'bar!'; + }); + } +} +``` + +The blog system creates an emitter instance and loads the plugins: +```php +$emitter = new EventEmitter(); + +$pluginClasses = json_decode(file_get_contents('plugins.json'), true); +foreach ($pluginClasses as $pluginClass) { + $plugin = new $pluginClass(); + $pluginClass->attachEvents($emitter); +} +``` + +This is the base system. There are no plugins yet, and there are no events yet +either. That's because I don't know which extension points will be needed. I +will add them on demand. + +## Feature: Markdown + +Writing blog posts in HTML sucks! Wouldn't it be great if I could write them +in a nice format such as markdown, and have that be converted to HTML for me? + +This feature will need two extension points. I need to be able to mark posts +as markdown, and I need to be able to hook into the rendering of the post body +and convert it from markdown to HTML. So the blog needs two new events: +`post.create` and `post.render`. + +In the code that creates the post, I'll insert the `post.create` event: +```php +class PostEvent +{ + public $post; + + public function __construct(array $post) + { + $this->post = $post; + } +} + +$post = createPostFromRequest($_POST); + +$event = new PostEvent($post); +$emitter->emit('post.create', [$event]); +$post = $event->post; + +$db->save('post', $post); +``` + +This shows that you can wrap a value in an event object to make it mutable, +allowing listeners to change it. + +The same thing for the `post.render` event: +```php +public function renderPostBody(array $post) +{ + $emitter = $this->emitter; + + $event = new PostEvent($post); + $emitter->emit('post.render', [$event]); + $post = $event->post; + + return $post['body']; +} + +

+

+``` + +Ok, the events are in place. It's time to create the first plugin, woohoo! I +will call this the `MarkdownPlugin`, so here's `plugins.json`: +```json +[ + "MarkdownPlugin" +] +``` + +The `MarkdownPlugin` class will be autoloaded, so I don't have to worry about +including any files. I just have to worry about implementing the plugin class. +The `markdown` function represents a markdown to HTML converter. +```php +class MarkdownPlugin implements PluginInterface +{ + public function attachEvents(EventEmitterInterface $emitter) + { + $emitter->on('post.create', function (PostEvent $event) { + $event->post['format'] = 'markdown'; + }); + + $emitter->on('post.render', function (PostEvent $event) { + if (isset($event->post['format']) && 'markdown' === $event->post['format']) { + $event->post['body'] = markdown($event->post['body']); + } + }); + } +} +``` + +There you go, the blog now renders posts as markdown. But all of the previous +posts before the addition of the markdown plugin are still rendered correctly +as raw HTML. + +## Feature: Comments + +TODO + +## Feature: Comment spam control + +TODO diff --git a/vendor/evenement/evenement/examples/benchmark-emit-no-arguments.php b/vendor/evenement/evenement/examples/benchmark-emit-no-arguments.php new file mode 100644 index 0000000000..53d7f4b225 --- /dev/null +++ b/vendor/evenement/evenement/examples/benchmark-emit-no-arguments.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +const ITERATIONS = 10000000; + +use Evenement\EventEmitter; + +require __DIR__.'/../vendor/autoload.php'; + +$emitter = new EventEmitter(); + +$emitter->on('event', function () {}); + +$start = microtime(true); +for ($i = 0; $i < ITERATIONS; $i++) { + $emitter->emit('event'); +} +$time = microtime(true) - $start; + +echo 'Emitting ', number_format(ITERATIONS), ' events took: ', number_format($time, 2), 's', PHP_EOL; diff --git a/vendor/evenement/evenement/examples/benchmark-emit-once.php b/vendor/evenement/evenement/examples/benchmark-emit-once.php new file mode 100644 index 0000000000..74f4d1755e --- /dev/null +++ b/vendor/evenement/evenement/examples/benchmark-emit-once.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +ini_set('memory_limit', '512M'); + +const ITERATIONS = 100000; + +use Evenement\EventEmitter; + +require __DIR__.'/../vendor/autoload.php'; + +$emitter = new EventEmitter(); + +for ($i = 0; $i < ITERATIONS; $i++) { + $emitter->once('event', function ($a, $b, $c) {}); +} + +$start = microtime(true); +$emitter->emit('event', [1, 2, 3]); +$time = microtime(true) - $start; + +echo 'Emitting one event to ', number_format(ITERATIONS), ' once listeners took: ', number_format($time, 2), 's', PHP_EOL; diff --git a/vendor/evenement/evenement/examples/benchmark-emit-one-argument.php b/vendor/evenement/evenement/examples/benchmark-emit-one-argument.php new file mode 100644 index 0000000000..39fc4ba09e --- /dev/null +++ b/vendor/evenement/evenement/examples/benchmark-emit-one-argument.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +const ITERATIONS = 10000000; + +use Evenement\EventEmitter; + +require __DIR__.'/../vendor/autoload.php'; + +$emitter = new EventEmitter(); + +$emitter->on('event', function ($a) {}); + +$start = microtime(true); +for ($i = 0; $i < ITERATIONS; $i++) { + $emitter->emit('event', [1]); +} +$time = microtime(true) - $start; + +echo 'Emitting ', number_format(ITERATIONS), ' events took: ', number_format($time, 2), 's', PHP_EOL; diff --git a/vendor/evenement/evenement/examples/benchmark-emit.php b/vendor/evenement/evenement/examples/benchmark-emit.php new file mode 100644 index 0000000000..3ab639e07a --- /dev/null +++ b/vendor/evenement/evenement/examples/benchmark-emit.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +const ITERATIONS = 10000000; + +use Evenement\EventEmitter; + +require __DIR__.'/../vendor/autoload.php'; + +$emitter = new EventEmitter(); + +$emitter->on('event', function ($a, $b, $c) {}); + +$start = microtime(true); +for ($i = 0; $i < ITERATIONS; $i++) { + $emitter->emit('event', [1, 2, 3]); +} +$time = microtime(true) - $start; + +echo 'Emitting ', number_format(ITERATIONS), ' events took: ', number_format($time, 2), 's', PHP_EOL; diff --git a/vendor/evenement/evenement/examples/benchmark-remove-listener-once.php b/vendor/evenement/evenement/examples/benchmark-remove-listener-once.php new file mode 100644 index 0000000000..414be3bd23 --- /dev/null +++ b/vendor/evenement/evenement/examples/benchmark-remove-listener-once.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +ini_set('memory_limit', '512M'); + +const ITERATIONS = 100000; + +use Evenement\EventEmitter; + +require __DIR__.'/../vendor/autoload.php'; + +$emitter = new EventEmitter(); + +$listeners = []; +for ($i = 0; $i < ITERATIONS; $i++) { + $listeners[] = function ($a, $b, $c) {}; +} + +$start = microtime(true); +foreach ($listeners as $listener) { + $emitter->once('event', $listener); +} +$time = microtime(true) - $start; +echo 'Adding ', number_format(ITERATIONS), ' once listeners took: ', number_format($time, 2), 's', PHP_EOL; + +$start = microtime(true); +foreach ($listeners as $listener) { + $emitter->removeListener('event', $listener); +} +$time = microtime(true) - $start; +echo 'Removing ', number_format(ITERATIONS), ' once listeners took: ', number_format($time, 2), 's', PHP_EOL; diff --git a/vendor/evenement/evenement/phpunit.xml.dist b/vendor/evenement/evenement/phpunit.xml.dist new file mode 100644 index 0000000000..70bc693a56 --- /dev/null +++ b/vendor/evenement/evenement/phpunit.xml.dist @@ -0,0 +1,24 @@ + + + + + + ./tests/Evenement/ + + + + + + ./src/ + + + diff --git a/vendor/evenement/evenement/src/Evenement/EventEmitter.php b/vendor/evenement/evenement/src/Evenement/EventEmitter.php new file mode 100644 index 0000000000..db189b972b --- /dev/null +++ b/vendor/evenement/evenement/src/Evenement/EventEmitter.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Evenement; + +class EventEmitter implements EventEmitterInterface +{ + use EventEmitterTrait; +} diff --git a/vendor/evenement/evenement/src/Evenement/EventEmitterInterface.php b/vendor/evenement/evenement/src/Evenement/EventEmitterInterface.php new file mode 100644 index 0000000000..310631a104 --- /dev/null +++ b/vendor/evenement/evenement/src/Evenement/EventEmitterInterface.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Evenement; + +interface EventEmitterInterface +{ + public function on($event, callable $listener); + public function once($event, callable $listener); + public function removeListener($event, callable $listener); + public function removeAllListeners($event = null); + public function listeners($event = null); + public function emit($event, array $arguments = []); +} diff --git a/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php b/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php new file mode 100644 index 0000000000..a78e65ca51 --- /dev/null +++ b/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Evenement; + +use InvalidArgumentException; + +trait EventEmitterTrait +{ + protected $listeners = []; + protected $onceListeners = []; + + public function on($event, callable $listener) + { + if ($event === null) { + throw new InvalidArgumentException('event name must not be null'); + } + + if (!isset($this->listeners[$event])) { + $this->listeners[$event] = []; + } + + $this->listeners[$event][] = $listener; + + return $this; + } + + public function once($event, callable $listener) + { + if ($event === null) { + throw new InvalidArgumentException('event name must not be null'); + } + + if (!isset($this->onceListeners[$event])) { + $this->onceListeners[$event] = []; + } + + $this->onceListeners[$event][] = $listener; + + return $this; + } + + public function removeListener($event, callable $listener) + { + if ($event === null) { + throw new InvalidArgumentException('event name must not be null'); + } + + if (isset($this->listeners[$event])) { + $index = \array_search($listener, $this->listeners[$event], true); + if (false !== $index) { + unset($this->listeners[$event][$index]); + if (\count($this->listeners[$event]) === 0) { + unset($this->listeners[$event]); + } + } + } + + if (isset($this->onceListeners[$event])) { + $index = \array_search($listener, $this->onceListeners[$event], true); + if (false !== $index) { + unset($this->onceListeners[$event][$index]); + if (\count($this->onceListeners[$event]) === 0) { + unset($this->onceListeners[$event]); + } + } + } + } + + public function removeAllListeners($event = null) + { + if ($event !== null) { + unset($this->listeners[$event]); + } else { + $this->listeners = []; + } + + if ($event !== null) { + unset($this->onceListeners[$event]); + } else { + $this->onceListeners = []; + } + } + + public function listeners($event = null): array + { + if ($event === null) { + $events = []; + $eventNames = \array_unique( + \array_merge(\array_keys($this->listeners), \array_keys($this->onceListeners)) + ); + foreach ($eventNames as $eventName) { + $events[$eventName] = \array_merge( + isset($this->listeners[$eventName]) ? $this->listeners[$eventName] : [], + isset($this->onceListeners[$eventName]) ? $this->onceListeners[$eventName] : [] + ); + } + return $events; + } + + return \array_merge( + isset($this->listeners[$event]) ? $this->listeners[$event] : [], + isset($this->onceListeners[$event]) ? $this->onceListeners[$event] : [] + ); + } + + public function emit($event, array $arguments = []) + { + if ($event === null) { + throw new InvalidArgumentException('event name must not be null'); + } + + if (isset($this->listeners[$event])) { + foreach ($this->listeners[$event] as $listener) { + $listener(...$arguments); + } + } + + if (isset($this->onceListeners[$event])) { + $listeners = $this->onceListeners[$event]; + unset($this->onceListeners[$event]); + foreach ($listeners as $listener) { + $listener(...$arguments); + } + } + } +} diff --git a/vendor/evenement/evenement/tests/Evenement/Tests/EventEmitterTest.php b/vendor/evenement/evenement/tests/Evenement/Tests/EventEmitterTest.php new file mode 100644 index 0000000000..28f3011d67 --- /dev/null +++ b/vendor/evenement/evenement/tests/Evenement/Tests/EventEmitterTest.php @@ -0,0 +1,438 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Evenement\Tests; + +use Evenement\EventEmitter; +use InvalidArgumentException; +use PHPUnit\Framework\TestCase; + +class EventEmitterTest extends TestCase +{ + private $emitter; + + public function setUp() + { + $this->emitter = new EventEmitter(); + } + + public function testAddListenerWithLambda() + { + $this->emitter->on('foo', function () {}); + } + + public function testAddListenerWithMethod() + { + $listener = new Listener(); + $this->emitter->on('foo', [$listener, 'onFoo']); + } + + public function testAddListenerWithStaticMethod() + { + $this->emitter->on('bar', ['Evenement\Tests\Listener', 'onBar']); + } + + public function testAddListenerWithInvalidListener() + { + try { + $this->emitter->on('foo', 'not a callable'); + $this->fail(); + } catch (\Exception $e) { + } catch (\TypeError $e) { + } + } + + public function testOnce() + { + $listenerCalled = 0; + + $this->emitter->once('foo', function () use (&$listenerCalled) { + $listenerCalled++; + }); + + $this->assertSame(0, $listenerCalled); + + $this->emitter->emit('foo'); + + $this->assertSame(1, $listenerCalled); + + $this->emitter->emit('foo'); + + $this->assertSame(1, $listenerCalled); + } + + public function testOnceWithArguments() + { + $capturedArgs = []; + + $this->emitter->once('foo', function ($a, $b) use (&$capturedArgs) { + $capturedArgs = array($a, $b); + }); + + $this->emitter->emit('foo', array('a', 'b')); + + $this->assertSame(array('a', 'b'), $capturedArgs); + } + + public function testEmitWithoutArguments() + { + $listenerCalled = false; + + $this->emitter->on('foo', function () use (&$listenerCalled) { + $listenerCalled = true; + }); + + $this->assertSame(false, $listenerCalled); + $this->emitter->emit('foo'); + $this->assertSame(true, $listenerCalled); + } + + public function testEmitWithOneArgument() + { + $test = $this; + + $listenerCalled = false; + + $this->emitter->on('foo', function ($value) use (&$listenerCalled, $test) { + $listenerCalled = true; + + $test->assertSame('bar', $value); + }); + + $this->assertSame(false, $listenerCalled); + $this->emitter->emit('foo', ['bar']); + $this->assertSame(true, $listenerCalled); + } + + public function testEmitWithTwoArguments() + { + $test = $this; + + $listenerCalled = false; + + $this->emitter->on('foo', function ($arg1, $arg2) use (&$listenerCalled, $test) { + $listenerCalled = true; + + $test->assertSame('bar', $arg1); + $test->assertSame('baz', $arg2); + }); + + $this->assertSame(false, $listenerCalled); + $this->emitter->emit('foo', ['bar', 'baz']); + $this->assertSame(true, $listenerCalled); + } + + public function testEmitWithNoListeners() + { + $this->emitter->emit('foo'); + $this->emitter->emit('foo', ['bar']); + $this->emitter->emit('foo', ['bar', 'baz']); + } + + public function testEmitWithTwoListeners() + { + $listenersCalled = 0; + + $this->emitter->on('foo', function () use (&$listenersCalled) { + $listenersCalled++; + }); + + $this->emitter->on('foo', function () use (&$listenersCalled) { + $listenersCalled++; + }); + + $this->assertSame(0, $listenersCalled); + $this->emitter->emit('foo'); + $this->assertSame(2, $listenersCalled); + } + + public function testRemoveListenerMatching() + { + $listenersCalled = 0; + + $listener = function () use (&$listenersCalled) { + $listenersCalled++; + }; + + $this->emitter->on('foo', $listener); + $this->emitter->removeListener('foo', $listener); + + $this->assertSame(0, $listenersCalled); + $this->emitter->emit('foo'); + $this->assertSame(0, $listenersCalled); + } + + public function testRemoveListenerNotMatching() + { + $listenersCalled = 0; + + $listener = function () use (&$listenersCalled) { + $listenersCalled++; + }; + + $this->emitter->on('foo', $listener); + $this->emitter->removeListener('bar', $listener); + + $this->assertSame(0, $listenersCalled); + $this->emitter->emit('foo'); + $this->assertSame(1, $listenersCalled); + } + + public function testRemoveAllListenersMatching() + { + $listenersCalled = 0; + + $this->emitter->on('foo', function () use (&$listenersCalled) { + $listenersCalled++; + }); + + $this->emitter->removeAllListeners('foo'); + + $this->assertSame(0, $listenersCalled); + $this->emitter->emit('foo'); + $this->assertSame(0, $listenersCalled); + } + + public function testRemoveAllListenersNotMatching() + { + $listenersCalled = 0; + + $this->emitter->on('foo', function () use (&$listenersCalled) { + $listenersCalled++; + }); + + $this->emitter->removeAllListeners('bar'); + + $this->assertSame(0, $listenersCalled); + $this->emitter->emit('foo'); + $this->assertSame(1, $listenersCalled); + } + + public function testRemoveAllListenersWithoutArguments() + { + $listenersCalled = 0; + + $this->emitter->on('foo', function () use (&$listenersCalled) { + $listenersCalled++; + }); + + $this->emitter->on('bar', function () use (&$listenersCalled) { + $listenersCalled++; + }); + + $this->emitter->removeAllListeners(); + + $this->assertSame(0, $listenersCalled); + $this->emitter->emit('foo'); + $this->emitter->emit('bar'); + $this->assertSame(0, $listenersCalled); + } + + public function testCallablesClosure() + { + $calledWith = null; + + $this->emitter->on('foo', function ($data) use (&$calledWith) { + $calledWith = $data; + }); + + $this->emitter->emit('foo', ['bar']); + + self::assertSame('bar', $calledWith); + } + + public function testCallablesClass() + { + $listener = new Listener(); + $this->emitter->on('foo', [$listener, 'onFoo']); + + $this->emitter->emit('foo', ['bar']); + + self::assertSame(['bar'], $listener->getData()); + } + + + public function testCallablesClassInvoke() + { + $listener = new Listener(); + $this->emitter->on('foo', $listener); + + $this->emitter->emit('foo', ['bar']); + + self::assertSame(['bar'], $listener->getMagicData()); + } + + public function testCallablesStaticClass() + { + $this->emitter->on('foo', '\Evenement\Tests\Listener::onBar'); + + $this->emitter->emit('foo', ['bar']); + + self::assertSame(['bar'], Listener::getStaticData()); + } + + public function testCallablesFunction() + { + $this->emitter->on('foo', '\Evenement\Tests\setGlobalTestData'); + + $this->emitter->emit('foo', ['bar']); + + self::assertSame('bar', $GLOBALS['evenement-evenement-test-data']); + + unset($GLOBALS['evenement-evenement-test-data']); + } + + public function testListeners() + { + $onA = function () {}; + $onB = function () {}; + $onC = function () {}; + $onceA = function () {}; + $onceB = function () {}; + $onceC = function () {}; + + self::assertCount(0, $this->emitter->listeners('event')); + $this->emitter->on('event', $onA); + self::assertCount(1, $this->emitter->listeners('event')); + self::assertSame([$onA], $this->emitter->listeners('event')); + $this->emitter->once('event', $onceA); + self::assertCount(2, $this->emitter->listeners('event')); + self::assertSame([$onA, $onceA], $this->emitter->listeners('event')); + $this->emitter->once('event', $onceB); + self::assertCount(3, $this->emitter->listeners('event')); + self::assertSame([$onA, $onceA, $onceB], $this->emitter->listeners('event')); + $this->emitter->on('event', $onB); + self::assertCount(4, $this->emitter->listeners('event')); + self::assertSame([$onA, $onB, $onceA, $onceB], $this->emitter->listeners('event')); + $this->emitter->removeListener('event', $onceA); + self::assertCount(3, $this->emitter->listeners('event')); + self::assertSame([$onA, $onB, $onceB], $this->emitter->listeners('event')); + $this->emitter->once('event', $onceC); + self::assertCount(4, $this->emitter->listeners('event')); + self::assertSame([$onA, $onB, $onceB, $onceC], $this->emitter->listeners('event')); + $this->emitter->on('event', $onC); + self::assertCount(5, $this->emitter->listeners('event')); + self::assertSame([$onA, $onB, $onC, $onceB, $onceC], $this->emitter->listeners('event')); + $this->emitter->once('event', $onceA); + self::assertCount(6, $this->emitter->listeners('event')); + self::assertSame([$onA, $onB, $onC, $onceB, $onceC, $onceA], $this->emitter->listeners('event')); + $this->emitter->removeListener('event', $onB); + self::assertCount(5, $this->emitter->listeners('event')); + self::assertSame([$onA, $onC, $onceB, $onceC, $onceA], $this->emitter->listeners('event')); + $this->emitter->emit('event'); + self::assertCount(2, $this->emitter->listeners('event')); + self::assertSame([$onA, $onC], $this->emitter->listeners('event')); + } + + public function testOnceCallIsNotRemovedWhenWorkingOverOnceListeners() + { + $aCalled = false; + $aCallable = function () use (&$aCalled) { + $aCalled = true; + }; + $bCalled = false; + $bCallable = function () use (&$bCalled, $aCallable) { + $bCalled = true; + $this->emitter->once('event', $aCallable); + }; + $this->emitter->once('event', $bCallable); + + self::assertFalse($aCalled); + self::assertFalse($bCalled); + $this->emitter->emit('event'); + + self::assertFalse($aCalled); + self::assertTrue($bCalled); + $this->emitter->emit('event'); + + self::assertTrue($aCalled); + self::assertTrue($bCalled); + } + + public function testEventNameMustBeStringOn() + { + self::expectException(InvalidArgumentException::class); + self::expectExceptionMessage('event name must not be null'); + + $this->emitter->on(null, function () {}); + } + + public function testEventNameMustBeStringOnce() + { + self::expectException(InvalidArgumentException::class); + self::expectExceptionMessage('event name must not be null'); + + $this->emitter->once(null, function () {}); + } + + public function testEventNameMustBeStringRemoveListener() + { + self::expectException(InvalidArgumentException::class); + self::expectExceptionMessage('event name must not be null'); + + $this->emitter->removeListener(null, function () {}); + } + + public function testEventNameMustBeStringEmit() + { + self::expectException(InvalidArgumentException::class); + self::expectExceptionMessage('event name must not be null'); + + $this->emitter->emit(null); + } + + public function testListenersGetAll() + { + $a = function () {}; + $b = function () {}; + $c = function () {}; + $d = function () {}; + + $this->emitter->once('event2', $c); + $this->emitter->on('event', $a); + $this->emitter->once('event', $b); + $this->emitter->on('event', $c); + $this->emitter->once('event', $d); + + self::assertSame( + [ + 'event' => [ + $a, + $c, + $b, + $d, + ], + 'event2' => [ + $c, + ], + ], + $this->emitter->listeners() + ); + } + + public function testOnceNestedCallRegression() + { + $first = 0; + $second = 0; + + $this->emitter->once('event', function () use (&$first, &$second) { + $first++; + $this->emitter->once('event', function () use (&$second) { + $second++; + }); + $this->emitter->emit('event'); + }); + $this->emitter->emit('event'); + + self::assertSame(1, $first); + self::assertSame(1, $second); + } +} diff --git a/vendor/evenement/evenement/tests/Evenement/Tests/Listener.php b/vendor/evenement/evenement/tests/Evenement/Tests/Listener.php new file mode 100644 index 0000000000..df17424656 --- /dev/null +++ b/vendor/evenement/evenement/tests/Evenement/Tests/Listener.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Evenement\Tests; + +class Listener +{ + private $data = []; + + private $magicData = []; + + private static $staticData = []; + + public function onFoo($data) + { + $this->data[] = $data; + } + + public function __invoke($data) + { + $this->magicData[] = $data; + } + + public static function onBar($data) + { + self::$staticData[] = $data; + } + + public function getData() + { + return $this->data; + } + + public function getMagicData() + { + return $this->magicData; + } + + public static function getStaticData() + { + return self::$staticData; + } +} diff --git a/vendor/evenement/evenement/tests/Evenement/Tests/functions.php b/vendor/evenement/evenement/tests/Evenement/Tests/functions.php new file mode 100644 index 0000000000..7f11f5ba92 --- /dev/null +++ b/vendor/evenement/evenement/tests/Evenement/Tests/functions.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Evenement\Tests; + +function setGlobalTestData($data) +{ + $GLOBALS['evenement-evenement-test-data'] = $data; +} diff --git a/vendor/lightsaml/lightsaml/.php_cs b/vendor/lightsaml/lightsaml/.php_cs new file mode 100644 index 0000000000..b212c9519c --- /dev/null +++ b/vendor/lightsaml/lightsaml/.php_cs @@ -0,0 +1,26 @@ +in('src') +; + +$header = << + +This source file is subject to the MIT license that is bundled +with this source code in the file LICENSE. +EOT; + +return PhpCsFixer\Config::create() + ->setRules(array( + '@Symfony' => true, + 'simplified_null_return' => false, + 'phpdoc_no_empty_return' => false, + 'no_mixed_echo_print' => ['use' => 'print'], + 'header_comment' => ['header' => $header], + )) + ->setUsingCache(false) + ->setFinder($finder) +; diff --git a/vendor/lightsaml/lightsaml/CHANGELOG-1.0.md b/vendor/lightsaml/lightsaml/CHANGELOG-1.0.md new file mode 100644 index 0000000000..ab31a64dbf --- /dev/null +++ b/vendor/lightsaml/lightsaml/CHANGELOG-1.0.md @@ -0,0 +1,20 @@ +CHANGELOG for 1.0.x +=================== + +1.0.2 (2014-07-09) +* [Bug] Logout Request Builder #5 +* [CodeStyle] Improve code metric #7 +* [Bug] Support for formatted certificate in message XML #10 +* [Bug] "KeyDescriptor" elment "use" attribute should be optional #12 +* [NewFeature] Support for EntitiesDescriptor element #11 +* [Bug] InResponseTo attribute optional for StatusResponse #14 #15 +* [Bug] InvalidArgumentException at LogoutRequest ->setNotOnOrAfter() #16 +* [NewFeature] New method in Signature Validator for array of keys #18 +* [NewFeature] New method EntitiesDescriptor::getByEntityId #19 +* [Bug] Fix AuthnRequest send and Response receive bidnings and url #20 +* [NewFeature] Logging of sent/received messages in Binding #23 +* [Bug] NameIDPolicy made optional in AuthnRequest? #21 +* [Bug] SignatureMethod element made optional #24 +* [Bug] StatusCode missing from status #26 +* [NewFeature] Optional constructor arguments 037a595fc +* [NewFeature] Support for IdpSsoDescriptor Attributes & NameIdFormat e37b037d1 diff --git a/vendor/lightsaml/lightsaml/CHANGELOG-2.0.md b/vendor/lightsaml/lightsaml/CHANGELOG-2.0.md new file mode 100644 index 0000000000..1e6e82c398 --- /dev/null +++ b/vendor/lightsaml/lightsaml/CHANGELOG-2.0.md @@ -0,0 +1,5 @@ +CHANGELOG for 2.0.x +=================== + +2.0.0 (2015-10-??) + * ... diff --git a/vendor/lightsaml/lightsaml/CONTRIBUTORS.md b/vendor/lightsaml/lightsaml/CONTRIBUTORS.md new file mode 100644 index 0000000000..7e629bc043 --- /dev/null +++ b/vendor/lightsaml/lightsaml/CONTRIBUTORS.md @@ -0,0 +1,8 @@ +CONTRIBUTORS +============ + +LightSaml so far is a work result of + +* Milos Tomic (tmilos) + + diff --git a/vendor/lightsaml/lightsaml/LICENSE b/vendor/lightsaml/lightsaml/LICENSE new file mode 100644 index 0000000000..e3426bbab2 --- /dev/null +++ b/vendor/lightsaml/lightsaml/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Milos Tomic + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/lightsaml/lightsaml/README.md b/vendor/lightsaml/lightsaml/README.md new file mode 100644 index 0000000000..54ce58b3cd --- /dev/null +++ b/vendor/lightsaml/lightsaml/README.md @@ -0,0 +1,26 @@ +SAML 2.0 PHP Library +==================== + +[![GitHub release](https://img.shields.io/github/release/lightsaml/lightsaml.svg)](https://github.com/lightSAML/lightSAML) +[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) +[![Build Status](https://travis-ci.org/lightSAML/lightSAML.svg?branch=master)](https://travis-ci.org/lightSAML/lightSAML) +[![Coverage Status](https://coveralls.io/repos/github/lightSAML/lightSAML/badge.svg?branch=master)](https://coveralls.io/github/lightSAML/lightSAML?branch=master) +[![Scrutinizer](https://scrutinizer-ci.com/g/lightSAML/lightSAML/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/lightSAML/lightSAML/?branch=master) +[![Packagist Version](https://img.shields.io/packagist/v/lightsaml/lightsaml.svg?style=flat-square)](https://packagist.org/packages/lightsaml/lightsaml) + +LightSaml Implements basic SAML 2.0 data model classes, serialization/deserialization to/from xml with XML security and +certificates support, and message encapsulations to bindings. Covered with unit tests. + + +DOCUMENTATION +============= + +* [Homepage](http://www.lightsaml.com/LightSAML-Core/) +* [Installation](http://www.lightsaml.com/LightSAML-Core/Installation/) +* [Cookbook](http://www.lightsaml.com/LightSAML-Core/Cookbook/) + + +CONTRIBUTING +============ + +LightSaml is an open source project and is open for contributions. diff --git a/vendor/lightsaml/lightsaml/autoload.php b/vendor/lightsaml/lightsaml/autoload.php new file mode 100644 index 0000000000..de9eb92604 --- /dev/null +++ b/vendor/lightsaml/lightsaml/autoload.php @@ -0,0 +1,8 @@ +=5.6", + "robrichards/xmlseclibs": "~2.0|~3.0|~4.0", + "symfony/http-foundation": "~2.3|~3.0|~4.0", + "symfony/event-dispatcher": "~2.3|~3.0|~4.0" + }, + "require-dev": { + "symfony/dom-crawler": "~2.3|~3.0|~4.0", + "symfony/css-selector": "~2.3|~3.0|~4.0", + "pimple/pimple": "~3.0", + "phpunit/phpunit": ">=5.7", + "monolog/monolog": "~1.3" + }, + "suggest": { + "lightsaml/symfony-bridge": "Symfony 2 build container bridge", + "lightsaml/sp-bundle": "Symfony 2 SP security bundle" + }, + "config": { + "bin-dir": "bin" + }, + "prefer-stable": true, + "minimum-stability": "stable" +} diff --git a/vendor/lightsaml/lightsaml/contrib/pre-commit b/vendor/lightsaml/lightsaml/contrib/pre-commit new file mode 100644 index 0000000000..f5e7c290d3 --- /dev/null +++ b/vendor/lightsaml/lightsaml/contrib/pre-commit @@ -0,0 +1,39 @@ +#!/bin/bash +# Pre-commit Git hook. +# Runs PHP CS Fixer on PHP files. +# +# If you absolutely must commit without testing, +# use: git commit --no-verify + +# This will check only staged files to be commited. +filenames=($(git diff --staged --name-only HEAD)) + +# This will set text to red in terminal. +text_red=`tput setaf 1` +# This will set the text to green in terminal. +text_green=`tput setaf 2` +# This will reset the terminal text to normal. +text_reset=`tput sgr0` + +numberFilesChanged="${#filenames[@]}" + +if [[ $numberFilesChanged > 0 ]]; +then + echo "$numberFilesChanged files were changed, running php-cs-fixer" + # PHP CS Fixer. + for i in "${filenames[@]}" + do + if [[ $i == *.php ]]; + then + php php-cs-fixer.phar fix $i + + if [ $? -ne 0 ]; + then + # File had some issues. Now it is fine. Add this file to git again. + git add $i + fi + fi + done +fi + +echo "${text_green}PHP CS Fixer finished execution successfully.${text_reset}" diff --git a/vendor/lightsaml/lightsaml/contrib/setup.sh b/vendor/lightsaml/lightsaml/contrib/setup.sh new file mode 100644 index 0000000000..9fa9c639af --- /dev/null +++ b/vendor/lightsaml/lightsaml/contrib/setup.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +if [ ! -f php-cs-fixer.phar ]; then + echo "The php-cs-fixer.phar is required... downloading..." + wget http://get.sensiolabs.org/php-cs-fixer.phar -O php-cs-fixer.phar || curl http://get.sensiolabs.org/php-cs-fixer.phar -o php-cs-fixer.phar || { echo >&2 "I require wget or curl but they are not installed. Aborting."; exit 1; } +fi + +# Copy the pre-commit hook to the current repository hooks directory. +cp contrib/pre-commit .git/hooks/pre-commit + +# Add execution permission for pre-commit file. +chmod +x .git/hooks/pre-commit \ No newline at end of file diff --git a/vendor/lightsaml/lightsaml/phpda.yml.dist b/vendor/lightsaml/lightsaml/phpda.yml.dist new file mode 100644 index 0000000000..5248b59450 --- /dev/null +++ b/vendor/lightsaml/lightsaml/phpda.yml.dist @@ -0,0 +1,22 @@ +mode: 'usage' # usage | call | inheritance +source: './src' +filePattern: '*.php' +ignore: ['Tests', 'Builder'] +formatter: 'PhpDA\Writer\Strategy\Json' +target: './phpda.json' +# referenceValidator: 'Fully\Qualified\Class\Name\To\ReferenceValidator' +groupLength: 1 +visitor: + - PhpDA\Parser\Visitor\TagCollector + - PhpDA\Parser\Visitor\SuperglobalCollector +# - PhpDA\Parser\Visitor\UnsupportedEvalCollector +# - PhpDA\Parser\Visitor\UnsupportedFuncCollector +# - PhpDA\Parser\Visitor\UnsupportedVarCollector +# - PhpDA\Parser\Visitor\UnsupportedGlobalCollector +# - PhpDA\Parser\Visitor\NamespacedStringCollector +# - PhpDA\Parser\Visitor\IocContainerAccessorCollector +visitorOptions: + PhpDA\Parser\Visitor\Required\DeclaredNamespaceCollector: {minDepth: 2, sliceLength: 3} + PhpDA\Parser\Visitor\Required\MetaNamespaceCollector: {minDepth: 2, sliceLength: 3} + PhpDA\Parser\Visitor\Required\UsedNamespaceCollector: {minDepth: 2, sliceLength: 3} + PhpDA\Parser\Visitor\TagCollector: {minDepth: 2, sliceLength: 3} diff --git a/vendor/lightsaml/lightsaml/phpunit.xml.dist b/vendor/lightsaml/lightsaml/phpunit.xml.dist new file mode 100644 index 0000000000..ea876e9f7b --- /dev/null +++ b/vendor/lightsaml/lightsaml/phpunit.xml.dist @@ -0,0 +1,40 @@ + + + + + + + + + + + + tests + + + + + + + + src + + + + + diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/ActionInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/ActionInterface.php new file mode 100644 index 0000000000..d62dbc59b7 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/ActionInterface.php @@ -0,0 +1,22 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action; + +use LightSaml\Context\ContextInterface; + +interface ActionInterface +{ + /** + * @param ContextInterface $context + */ + public function execute(ContextInterface $context); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/ActionLogWrapper.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/ActionLogWrapper.php new file mode 100644 index 0000000000..95f4101786 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/ActionLogWrapper.php @@ -0,0 +1,40 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action; + +use Psr\Log\LoggerInterface; + +class ActionLogWrapper implements ActionWrapperInterface +{ + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param LoggerInterface $logger + */ + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * @param ActionInterface $action + * + * @return ActionInterface + */ + public function wrap(ActionInterface $action) + { + return new LoggableAction($action, $this->logger); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/ActionWrapperInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/ActionWrapperInterface.php new file mode 100644 index 0000000000..54c4b466f5 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/ActionWrapperInterface.php @@ -0,0 +1,22 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action; + +interface ActionWrapperInterface +{ + /** + * @param ActionInterface $action + * + * @return ActionInterface + */ + public function wrap(ActionInterface $action); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/AbstractAssertionAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/AbstractAssertionAction.php new file mode 100644 index 0000000000..ee1a031ecf --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/AbstractAssertionAction.php @@ -0,0 +1,49 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Assertion; + +use LightSaml\Action\ActionInterface; +use LightSaml\Context\ContextInterface; +use LightSaml\Context\Profile\AssertionContext; +use LightSaml\Error\LightSamlContextException; +use Psr\Log\LoggerInterface; + +abstract class AbstractAssertionAction implements ActionInterface +{ + /** @var LoggerInterface */ + protected $logger; + + /** + * @param LoggerInterface $logger + */ + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * @param ContextInterface $context + */ + public function execute(ContextInterface $context) + { + if ($context instanceof AssertionContext) { + $this->doExecute($context); + } else { + throw new LightSamlContextException($context, 'Expected AssertionContext'); + } + } + + /** + * @param AssertionContext $context + */ + abstract protected function doExecute(AssertionContext $context); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/AssertionIssuerFormatValidatorAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/AssertionIssuerFormatValidatorAction.php new file mode 100644 index 0000000000..f534204739 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/AssertionIssuerFormatValidatorAction.php @@ -0,0 +1,63 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Assertion\Inbound; + +use LightSaml\Action\Assertion\AbstractAssertionAction; +use LightSaml\Context\Profile\AssertionContext; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Error\LightSamlContextException; +use LightSaml\SamlConstants; +use Psr\Log\LoggerInterface; + +class AssertionIssuerFormatValidatorAction extends AbstractAssertionAction +{ + /** @var string */ + private $expectedIssuerFormat = SamlConstants::NAME_ID_FORMAT_ENTITY; + + /** + * @param LoggerInterface $logger + * @param string $expectedIssuerFormat + */ + public function __construct(LoggerInterface $logger, $expectedIssuerFormat) + { + parent::__construct($logger); + + $this->expectedIssuerFormat = $expectedIssuerFormat; + } + + /** + * @param AssertionContext $context + */ + protected function doExecute(AssertionContext $context) + { + if (null == $context->getAssertion()->getIssuer()) { + $message = 'Assertion element must have an issuer element'; + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + if ($context->getAssertion()->getIssuer()->getFormat() && + $context->getAssertion()->getIssuer()->getFormat() != $this->expectedIssuerFormat + ) { + $message = sprintf( + "Response Issuer Format if set must have value '%s' but it was '%s'", + $this->expectedIssuerFormat, + $context->getAssertion()->getIssuer()->getFormat() + ); + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this, [ + 'actualFormat' => $context->getAssertion()->getIssuer()->getFormat(), + 'expectedFormat' => $this->expectedIssuerFormat, + ])); + throw new LightSamlContextException($context, $message); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/AssertionSignatureValidatorAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/AssertionSignatureValidatorAction.php new file mode 100644 index 0000000000..3418347d34 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/AssertionSignatureValidatorAction.php @@ -0,0 +1,89 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Assertion\Inbound; + +use LightSaml\Action\Assertion\AbstractAssertionAction; +use LightSaml\Context\Profile\AssertionContext; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Credential\Criteria\MetadataCriteria; +use LightSaml\Error\LightSamlContextException; +use LightSaml\Error\LightSamlModelException; +use LightSaml\Model\XmlDSig\AbstractSignatureReader; +use LightSaml\Validator\Model\Signature\SignatureValidatorInterface; +use Psr\Log\LoggerInterface; + +class AssertionSignatureValidatorAction extends AbstractAssertionAction +{ + /** @var SignatureValidatorInterface */ + protected $signatureValidator; + + /** @var bool */ + protected $requireSignature; + + /** + * @param LoggerInterface $logger + * @param SignatureValidatorInterface $signatureValidator + * @param bool $requireSignature + */ + public function __construct(LoggerInterface $logger, SignatureValidatorInterface $signatureValidator, $requireSignature = true) + { + parent::__construct($logger); + + $this->signatureValidator = $signatureValidator; + $this->requireSignature = $requireSignature; + } + + /** + * @param AssertionContext $context + * + * @return void + */ + protected function doExecute(AssertionContext $context) + { + $signature = $context->getAssertion()->getSignature(); + if (null === $signature) { + if ($this->requireSignature) { + $message = 'Assertions must be signed'; + $this->logger->critical($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } else { + $this->logger->debug('Assertion is not signed', LogHelper::getActionContext($context, $this)); + + return; + } + } + + if ($signature instanceof AbstractSignatureReader) { + $metadataType = ProfileContext::ROLE_IDP === $context->getProfileContext()->getOwnRole() ? MetadataCriteria::TYPE_SP : MetadataCriteria::TYPE_IDP; + $credential = $this->signatureValidator->validate($signature, $context->getAssertion()->getIssuer()->getValue(), $metadataType); + if ($credential) { + $keyNames = $credential->getKeyNames(); + $this->logger->debug( + sprintf('Assertion signature validated with key "%s"', implode(', ', $keyNames)), + LogHelper::getActionContext($context, $this, array( + 'credential' => $credential, + )) + ); + } else { + $this->logger->warning( + 'Assertion signature verification was not performed', + LogHelper::getActionContext($context, $this) + ); + } + } else { + $message = 'Expected AbstractSignatureReader'; + $this->logger->critical($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlModelException($message); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/AssertionValidatorAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/AssertionValidatorAction.php new file mode 100644 index 0000000000..9cb7ff496c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/AssertionValidatorAction.php @@ -0,0 +1,42 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Assertion\Inbound; + +use LightSaml\Action\Assertion\AbstractAssertionAction; +use LightSaml\Context\Profile\AssertionContext; +use LightSaml\Validator\Model\Assertion\AssertionValidatorInterface; +use Psr\Log\LoggerInterface; + +class AssertionValidatorAction extends AbstractAssertionAction +{ + /** @var AssertionValidatorInterface */ + protected $assertionValidator; + + /** + * @param LoggerInterface $logger + * @param AssertionValidatorInterface $assertionValidator + */ + public function __construct(LoggerInterface $logger, AssertionValidatorInterface $assertionValidator) + { + parent::__construct($logger); + + $this->assertionValidator = $assertionValidator; + } + + /** + * @param AssertionContext $context + */ + protected function doExecute(AssertionContext $context) + { + $this->assertionValidator->validateAssertion($context->getAssertion()); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/InResponseToValidatorAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/InResponseToValidatorAction.php new file mode 100644 index 0000000000..11462fac2b --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/InResponseToValidatorAction.php @@ -0,0 +1,81 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Assertion\Inbound; + +use LightSaml\Action\Assertion\AbstractAssertionAction; +use LightSaml\Context\Profile\AssertionContext; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\ProfileContexts; +use LightSaml\Context\Profile\RequestStateContext; +use LightSaml\Error\LightSamlContextException; +use LightSaml\Store\Request\RequestStateStoreInterface; +use Psr\Log\LoggerInterface; + +class InResponseToValidatorAction extends AbstractAssertionAction +{ + /** @var RequestStateStoreInterface */ + protected $requestStore; + + /** + * @param LoggerInterface $logger + * @param RequestStateStoreInterface $requestStore + */ + public function __construct(LoggerInterface $logger, RequestStateStoreInterface $requestStore) + { + parent::__construct($logger); + + $this->requestStore = $requestStore; + } + + /** + * @param AssertionContext $context + */ + protected function doExecute(AssertionContext $context) + { + if (null === $context->getAssertion()->getSubject()) { + return; + } + + foreach ($context->getAssertion()->getSubject()->getAllSubjectConfirmations() as $subjectConfirmation) { + if ($subjectConfirmation->getSubjectConfirmationData() && + $subjectConfirmation->getSubjectConfirmationData()->getInResponseTo() + ) { + $requestState = $this->validateInResponseTo( + $subjectConfirmation->getSubjectConfirmationData()->getInResponseTo(), + $context + ); + + /** @var RequestStateContext $requestStateContext */ + $requestStateContext = $context->getSubContext(ProfileContexts::REQUEST_STATE, RequestStateContext::class); + $requestStateContext->setRequestState($requestState); + } + } + } + + /** + * @param string $inResponseTo + * @param AssertionContext $context + * + * @return \LightSaml\State\Request\RequestState + */ + protected function validateInResponseTo($inResponseTo, AssertionContext $context) + { + $requestState = $this->requestStore->get($inResponseTo); + if (null == $requestState) { + $message = sprintf("Unknown InResponseTo '%s'", $inResponseTo); + $this->logger->emergency($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + return $requestState; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/KnownAssertionIssuerAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/KnownAssertionIssuerAction.php new file mode 100644 index 0000000000..4aa469911c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/KnownAssertionIssuerAction.php @@ -0,0 +1,63 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Assertion\Inbound; + +use LightSaml\Action\Assertion\AbstractAssertionAction; +use LightSaml\Context\Profile\AssertionContext; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Error\LightSamlContextException; +use LightSaml\Store\EntityDescriptor\EntityDescriptorStoreInterface; +use Psr\Log\LoggerInterface; + +class KnownAssertionIssuerAction extends AbstractAssertionAction +{ + /** @var EntityDescriptorStoreInterface */ + private $idpEntityDescriptorProvider; + + /** + * @param LoggerInterface $logger + * @param EntityDescriptorStoreInterface $idpEntityDescriptorProvider + */ + public function __construct(LoggerInterface $logger, EntityDescriptorStoreInterface $idpEntityDescriptorProvider) + { + parent::__construct($logger); + + $this->idpEntityDescriptorProvider = $idpEntityDescriptorProvider; + } + + /** + * @param AssertionContext $context + * + * @return void + */ + protected function doExecute(AssertionContext $context) + { + if (null === $context->getAssertion()->getIssuer()) { + $message = 'Assertion element must have an issuer element'; + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + if (false == $this->idpEntityDescriptorProvider->has($context->getAssertion()->getIssuer()->getValue())) { + $message = sprintf("Unknown issuer '%s'", $context->getAssertion()->getIssuer()->getValue()); + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this, [ + 'messageIssuer' => $context->getAssertion()->getIssuer()->getValue(), + ])); + throw new LightSamlContextException($context, $message); + } + + $this->logger->debug( + sprintf('Known assertion issuer: "%s"', $context->getAssertion()->getIssuer()->getValue()), + LogHelper::getActionContext($context, $this) + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/RecipientValidatorAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/RecipientValidatorAction.php new file mode 100644 index 0000000000..f5be441213 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/RecipientValidatorAction.php @@ -0,0 +1,95 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Assertion\Inbound; + +use LightSaml\Action\Assertion\AbstractAssertionAction; +use LightSaml\Context\Profile\AssertionContext; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Criteria\CriteriaSet; +use LightSaml\Error\LightSamlContextException; +use LightSaml\Model\Assertion\SubjectConfirmation; +use LightSaml\Model\Metadata\AssertionConsumerService; +use LightSaml\Model\Metadata\SpSsoDescriptor; +use LightSaml\Resolver\Endpoint\Criteria\DescriptorTypeCriteria; +use LightSaml\Resolver\Endpoint\Criteria\LocationCriteria; +use LightSaml\Resolver\Endpoint\Criteria\ServiceTypeCriteria; +use LightSaml\Resolver\Endpoint\EndpointResolverInterface; +use Psr\Log\LoggerInterface; + +class RecipientValidatorAction extends AbstractAssertionAction +{ + /** @var EndpointResolverInterface */ + private $endpointResolver; + + /** + * @param LoggerInterface $logger + * @param EndpointResolverInterface $endpointResolver + */ + public function __construct(LoggerInterface $logger, EndpointResolverInterface $endpointResolver) + { + parent::__construct($logger); + + $this->endpointResolver = $endpointResolver; + } + + /** + * @param AssertionContext $context + * + * @return void + */ + protected function doExecute(AssertionContext $context) + { + if ($context->getAssertion()->getAllAuthnStatements() && $context->getAssertion()->hasBearerSubject()) { + $this->validateBearerAssertion($context); + } + } + + /** + * @param AssertionContext $context + */ + protected function validateBearerAssertion(AssertionContext $context) + { + foreach ($context->getAssertion()->getSubject()->getBearerConfirmations() as $subjectConfirmation) { + $this->validateSubjectConfirmation($context, $subjectConfirmation); + } + } + + /** + * @param AssertionContext $context + * @param SubjectConfirmation $subjectConfirmation + */ + protected function validateSubjectConfirmation(AssertionContext $context, SubjectConfirmation $subjectConfirmation) + { + $recipient = $subjectConfirmation->getSubjectConfirmationData()->getRecipient(); + if (null == $recipient) { + $message = 'Bearer SubjectConfirmation must contain Recipient attribute'; + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + $criteriaSet = new CriteriaSet([ + new DescriptorTypeCriteria(SpSsoDescriptor::class), + new ServiceTypeCriteria(AssertionConsumerService::class), + new LocationCriteria($recipient), + ]); + $ownEntityDescriptor = $context->getProfileContext()->getOwnEntityDescriptor(); + $arrEndpoints = $this->endpointResolver->resolve($criteriaSet, $ownEntityDescriptor->getAllEndpoints()); + + if (empty($arrEndpoints)) { + $message = sprintf("Recipient '%s' does not match SP descriptor", $recipient); + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this, [ + 'recipient' => $recipient, + ])); + throw new LightSamlContextException($context, $message); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/RepeatedIdValidatorAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/RepeatedIdValidatorAction.php new file mode 100644 index 0000000000..d1526c5382 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/RepeatedIdValidatorAction.php @@ -0,0 +1,138 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Assertion\Inbound; + +use LightSaml\Action\Assertion\AbstractAssertionAction; +use LightSaml\Context\Profile\AssertionContext; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Error\LightSamlContextException; +use LightSaml\Store\Id\IdStoreInterface; +use Psr\Log\LoggerInterface; + +/** + * 4.1.4.5 POST-Specific Processing Rules + * The service provider MUST ensure that bearer assertions are not replayed, by maintaining the set of used + * ID values for the length of time for which the assertion would be considered valid based on the + * NotOnOrAfter attribute in the . + */ +class RepeatedIdValidatorAction extends AbstractAssertionAction +{ + /** @var IdStoreInterface */ + protected $idStore; + + /** + * @param LoggerInterface $logger + * @param IdStoreInterface $idStore + */ + public function __construct(LoggerInterface $logger, IdStoreInterface $idStore) + { + parent::__construct($logger); + + $this->idStore = $idStore; + } + + /** + * @param AssertionContext $context + * + * @return void + */ + protected function doExecute(AssertionContext $context) + { + if ($context->getAssertion()->hasBearerSubject()) { + $this->validateBearerAssertion($context); + } + } + + /** + * @param AssertionContext $context + * + * @throws \LightSaml\Error\LightSamlContextException + */ + protected function validateBearerAssertion(AssertionContext $context) + { + if (null == $context->getAssertion()->getId()) { + $message = 'Bearer Assertion must have ID attribute'; + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + if (null == $context->getAssertion()->getIssuer()) { + $message = 'Bearer Assertion must have Issuer element'; + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + if ($this->idStore->has($context->getAssertion()->getIssuer()->getValue(), $context->getAssertion()->getId())) { + $message = sprintf( + "Repeated assertion id '%s' of issuer '%s'", + $context->getAssertion()->getId(), + $context->getAssertion()->getIssuer()->getValue() + ); + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this, [ + 'id' => $context->getAssertion()->getId(), + 'issuer' => $context->getAssertion()->getIssuer()->getValue(), + ])); + throw new LightSamlContextException($context, $message); + } + + $this->idStore->set( + $context->getAssertion()->getIssuer()->getValue(), + $context->getAssertion()->getId(), + $this->getIdExpiryTime($context) + ); + } + + /** + * @param AssertionContext $context + * + * @throws \LogicException + * @throws \LightSaml\Error\LightSamlValidationException + * + * @return \DateTime + */ + protected function getIdExpiryTime(AssertionContext $context) + { + /** @var \DateTime $result */ + $result = null; + $bearerConfirmations = $context->getAssertion()->getSubject()->getBearerConfirmations(); + if (null == $bearerConfirmations) { + throw new \LogicException('Bearer assertion must have bearer subject confirmations'); + } + + foreach ($bearerConfirmations as $subjectConfirmation) { + if (null == $subjectConfirmation->getSubjectConfirmationData()) { + $message = 'Bearer SubjectConfirmation must have SubjectConfirmationData element'; + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + $dt = $subjectConfirmation->getSubjectConfirmationData()->getNotOnOrAfterDateTime(); + if (null == $dt) { + $message = 'Bearer SubjectConfirmation must have NotOnOrAfter attribute'; + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + if (null == $result || $result->getTimestamp() < $dt->getTimestamp()) { + $result = $dt; + } + } + + if (null == $result) { + $message = 'Unable to find NotOnOrAfter attribute in bearer assertion'; + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/TimeValidatorAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/TimeValidatorAction.php new file mode 100644 index 0000000000..b5611e6e88 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Assertion/Inbound/TimeValidatorAction.php @@ -0,0 +1,63 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Assertion\Inbound; + +use LightSaml\Action\Assertion\AbstractAssertionAction; +use LightSaml\Context\Profile\AssertionContext; +use LightSaml\Provider\TimeProvider\TimeProviderInterface; +use LightSaml\Validator\Model\Assertion\AssertionTimeValidatorInterface; +use Psr\Log\LoggerInterface; + +class TimeValidatorAction extends AbstractAssertionAction +{ + /** @var AssertionTimeValidatorInterface */ + protected $assertionTimeValidator; + + /** @var TimeProviderInterface */ + protected $timeProvider; + + /** @var int */ + protected $allowedSecondsSkew; + + /** + * @param LoggerInterface $logger + * @param AssertionTimeValidatorInterface $assertionTimeValidator + * @param TimeProviderInterface $timeProvider + * @param int $allowedSecondsSkew + */ + public function __construct( + LoggerInterface $logger, + AssertionTimeValidatorInterface $assertionTimeValidator, + TimeProviderInterface $timeProvider, + $allowedSecondsSkew = 120 + ) { + parent::__construct($logger); + + $this->assertionTimeValidator = $assertionTimeValidator; + $this->timeProvider = $timeProvider; + $this->allowedSecondsSkew = $allowedSecondsSkew; + } + + /** + * @param AssertionContext $context + * + * @return void + */ + protected function doExecute(AssertionContext $context) + { + $this->assertionTimeValidator->validateTimeRestrictions( + $context->getAssertion(), + $this->timeProvider->getTimestamp(), + $this->allowedSecondsSkew + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/CatchableErrorAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/CatchableErrorAction.php new file mode 100644 index 0000000000..9b8dee78d2 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/CatchableErrorAction.php @@ -0,0 +1,53 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action; + +use LightSaml\Context\ContextInterface; +use LightSaml\Context\Profile\ExceptionContext; +use LightSaml\Context\Profile\ProfileContexts; + +class CatchableErrorAction implements ActionInterface +{ + /** @var ActionInterface */ + protected $mainAction; + + /** @var ActionInterface */ + protected $errorAction; + + /** + * @param ActionInterface $mainAction + * @param ActionInterface $errorAction + */ + public function __construct(ActionInterface $mainAction, ActionInterface $errorAction) + { + $this->mainAction = $mainAction; + $this->errorAction = $errorAction; + } + + /** + * @param ContextInterface $context + * + * @return void + */ + public function execute(ContextInterface $context) + { + try { + $this->mainAction->execute($context); + } catch (\Exception $ex) { + /** @var ExceptionContext $exceptionContext */ + $exceptionContext = $context->getSubContext(ProfileContexts::EXCEPTION, ExceptionContext::class); + $exceptionContext->addException($ex); + + $this->errorAction->execute($context); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/CompositeAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/CompositeAction.php new file mode 100644 index 0000000000..085de86fc6 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/CompositeAction.php @@ -0,0 +1,106 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action; + +use LightSaml\Context\ContextInterface; + +class CompositeAction implements ActionInterface, DebugPrintTreeActionInterface, CompositeActionInterface +{ + /** @var ActionInterface[] */ + protected $children = array(); + + /** + * @param ActionInterface[] $children + */ + public function __construct(array $children = array()) + { + foreach ($children as $action) { + $this->add($action); + } + } + + /** + * @return ActionInterface[] + */ + public function getChildren() + { + return $this->children; + } + + /** + * @param ActionInterface $action + * + * @return CompositeAction + */ + public function add(ActionInterface $action) + { + $this->children[] = $action; + + return $this; + } + + /** + * @param callable $callable + * + * @return ActionInterface|null + */ + public function map($callable) + { + foreach ($this->children as $k => $action) { + $newAction = call_user_func($callable, $action); + if ($newAction) { + $this->children[$k] = $newAction; + } + } + } + + /** + * @param ContextInterface $context + * + * @return void + */ + public function execute(ContextInterface $context) + { + foreach ($this->children as $action) { + $action->execute($context); + } + } + + /** + * @return array + */ + public function debugPrintTree() + { + $arr = array(); + foreach ($this->children as $childAction) { + if ($childAction instanceof DebugPrintTreeActionInterface) { + $arr = array_merge($arr, $childAction->debugPrintTree()); + } else { + $arr = array_merge($arr, array(get_class($childAction) => array())); + } + } + + $result = array( + static::class => $arr, + ); + + return $result; + } + + /** + * @return string + */ + public function __toString() + { + return json_encode($this->debugPrintTree(), JSON_PRETTY_PRINT); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/CompositeActionInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/CompositeActionInterface.php new file mode 100644 index 0000000000..e947f44428 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/CompositeActionInterface.php @@ -0,0 +1,29 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action; + +interface CompositeActionInterface extends ActionInterface +{ + /** + * @param ActionInterface $action + * + * @return CompositeActionInterface + */ + public function add(ActionInterface $action); + + /** + * @param callable $callable + * + * @return ActionInterface|null + */ + public function map($callable); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/DebugPrintTreeActionInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/DebugPrintTreeActionInterface.php new file mode 100644 index 0000000000..47862b4ff7 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/DebugPrintTreeActionInterface.php @@ -0,0 +1,20 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action; + +interface DebugPrintTreeActionInterface +{ + /** + * @return array + */ + public function debugPrintTree(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/DispatchEventAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/DispatchEventAction.php new file mode 100644 index 0000000000..44e1aa2f93 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/DispatchEventAction.php @@ -0,0 +1,45 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action; + +use LightSaml\Context\ContextInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\GenericEvent; + +class DispatchEventAction implements ActionInterface +{ + /** @var EventDispatcherInterface */ + protected $eventDispatcher; + + /** @var string */ + protected $event; + + /** + * @param EventDispatcherInterface $eventDispatcher + * @param string $event + */ + public function __construct(EventDispatcherInterface $eventDispatcher, $event) + { + $this->eventDispatcher = $eventDispatcher; + $this->event = $event; + } + + /** + * @param ContextInterface $context + * + * @return void + */ + public function execute(ContextInterface $context) + { + $this->eventDispatcher->dispatch($this->event, new GenericEvent($context)); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/LoggableAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/LoggableAction.php new file mode 100644 index 0000000000..6c04ec104c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/LoggableAction.php @@ -0,0 +1,52 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action; + +use LightSaml\Context\ContextInterface; +use Psr\Log\LoggerInterface; + +class LoggableAction extends WrappedAction +{ + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param ActionInterface $action + * @param LoggerInterface $logger + */ + public function __construct(ActionInterface $action, LoggerInterface $logger) + { + parent::__construct($action); + + $this->logger = $logger; + } + + /** + * @param ContextInterface $context + */ + protected function beforeAction(ContextInterface $context) + { + $this->logger->debug(sprintf('Executing action "%s"', get_class($this->action)), array( + 'context' => $context, + 'action' => $this->action, + )); + } + + /** + * @param ContextInterface $context + */ + protected function afterAction(ContextInterface $context) + { + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/NullAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/NullAction.php new file mode 100644 index 0000000000..cbc1d8ac3f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/NullAction.php @@ -0,0 +1,27 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action; + +use LightSaml\Context\ContextInterface; + +class NullAction implements ActionInterface +{ + /** + * @param ContextInterface $context + * + * @return void + */ + public function execute(ContextInterface $context) + { + // null + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/AbstractProfileAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/AbstractProfileAction.php new file mode 100644 index 0000000000..9c74ca6c47 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/AbstractProfileAction.php @@ -0,0 +1,50 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile; + +use LightSaml\Action\ActionInterface; +use LightSaml\Context\ContextInterface; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Error\LightSamlContextException; +use Psr\Log\LoggerInterface; + +abstract class AbstractProfileAction implements ActionInterface +{ + /** @var LoggerInterface */ + protected $logger; + + /** + * @param LoggerInterface $logger + */ + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * @param ContextInterface $context + * + * @return void + */ + public function execute(ContextInterface $context) + { + if ($context instanceof ProfileContext) { + $this->doExecute($context); + } else { + $message = sprintf('Expected ProfileContext but got %s', get_class($context)); + $this->logger->emergency($message, array('context' => $context)); + throw new LightSamlContextException($context, $message); + } + } + + abstract protected function doExecute(ProfileContext $context); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Entity/SerializeOwnEntityAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Entity/SerializeOwnEntityAction.php new file mode 100644 index 0000000000..a69dcf5461 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Entity/SerializeOwnEntityAction.php @@ -0,0 +1,55 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Entity; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Context\Profile\ProfileContexts; +use LightSaml\Model\Context\SerializationContext; +use Symfony\Component\HttpFoundation\Response; + +class SerializeOwnEntityAction extends AbstractProfileAction +{ + /** @var string[] */ + protected $supportedContextTypes = array('application/samlmetadata+xml', 'application/xml', 'text/xml'); + + /** + * @param ProfileContext $context + */ + protected function doExecute(ProfileContext $context) + { + $ownEntityDescriptor = $context->getOwnEntityDescriptor(); + + /** @var SerializationContext $serializationContext */ + $serializationContext = $context->getSubContext(ProfileContexts::SERIALIZATION, SerializationContext::class); + $serializationContext->getDocument()->formatOutput = true; + + $ownEntityDescriptor->serialize($serializationContext->getDocument(), $serializationContext); + + $xml = $serializationContext->getDocument()->saveXML(); + + $response = new Response($xml); + + $contentType = 'text/xml'; + $acceptableContentTypes = array_flip($context->getHttpRequest()->getAcceptableContentTypes()); + foreach ($this->supportedContextTypes as $supportedContentType) { + if (isset($acceptableContentTypes[$supportedContentType])) { + $contentType = $supportedContentType; + break; + } + } + + $response->headers->replace(array('Content-Type' => $contentType)); + + $context->getHttpResponseContext()->setResponse($response); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/FlushRequestStatesAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/FlushRequestStatesAction.php new file mode 100644 index 0000000000..b8a50eb658 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/FlushRequestStatesAction.php @@ -0,0 +1,78 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile; + +use LightSaml\Context\ContextInterface; +use LightSaml\Context\Profile\AssertionContext; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\ProfileContexts; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Context\Profile\RequestStateContext; +use LightSaml\Store\Request\RequestStateStoreInterface; +use Psr\Log\LoggerInterface; + +class FlushRequestStatesAction extends AbstractProfileAction +{ + /** @var RequestStateStoreInterface */ + protected $requestStore; + + /** + * @param LoggerInterface $logger + * @param RequestStateStoreInterface $requestStore + */ + public function __construct(LoggerInterface $logger, RequestStateStoreInterface $requestStore) + { + parent::__construct($logger); + + $this->requestStore = $requestStore; + } + + /** + * @param ProfileContext $context + * + * @return void + */ + protected function doExecute(ProfileContext $context) + { + $this->flush($context->getInboundContext()->getSubContext(ProfileContexts::REQUEST_STATE, null)); + foreach ($context as $child) { + if ($child instanceof AssertionContext) { + $this->flush($child->getSubContext(ProfileContexts::REQUEST_STATE, null)); + } + } + } + + /** + * @param ContextInterface|null $requestStateContext + */ + protected function flush($requestStateContext = null) + { + if ($requestStateContext instanceof RequestStateContext && + $requestStateContext->getRequestState() && + $requestStateContext->getRequestState()->getId() + ) { + $existed = $this->requestStore->remove($requestStateContext->getRequestState()->getId()); + + if ($existed) { + $this->logger->debug( + sprintf('Removed request state "%s"', $requestStateContext->getRequestState()->getId()), + LogHelper::getActionContext($requestStateContext, $this) + ); + } else { + $this->logger->warning( + sprintf('Request state "%s" does not exist', $requestStateContext->getRequestState()->getId()), + LogHelper::getActionContext($requestStateContext, $this) + ); + } + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/AbstractDestinationValidatorAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/AbstractDestinationValidatorAction.php new file mode 100644 index 0000000000..ab2bb2e789 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/AbstractDestinationValidatorAction.php @@ -0,0 +1,88 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Criteria\CriteriaSet; +use LightSaml\Error\LightSamlContextException; +use LightSaml\Model\Metadata\IdpSsoDescriptor; +use LightSaml\Model\Metadata\SpSsoDescriptor; +use LightSaml\Resolver\Endpoint\Criteria\DescriptorTypeCriteria; +use LightSaml\Resolver\Endpoint\Criteria\LocationCriteria; +use LightSaml\Resolver\Endpoint\EndpointResolverInterface; +use Psr\Log\LoggerInterface; + +abstract class AbstractDestinationValidatorAction extends AbstractProfileAction +{ + /** @var EndpointResolverInterface */ + protected $endpointResolver; + + /** + * @param LoggerInterface $logger + * @param EndpointResolverInterface $endpointResolver + */ + public function __construct(LoggerInterface $logger, EndpointResolverInterface $endpointResolver) + { + parent::__construct($logger); + + $this->endpointResolver = $endpointResolver; + } + + /** + * @param ProfileContext $context + * + * @return void + */ + protected function doExecute(ProfileContext $context) + { + $message = MessageContextHelper::asSamlMessage($context->getInboundContext()); + $destination = $message->getDestination(); + + if (null == $destination) { + return; + } + + $criteriaSet = $this->getCriteriaSet($context, $destination); + $endpoints = $this->endpointResolver->resolve($criteriaSet, $context->getOwnEntityDescriptor()->getAllEndpoints()); + + if ($endpoints) { + return; + } + + $message = sprintf('Invalid inbound message destination "%s"', $destination); + $this->logger->emergency($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + /** + * @param ProfileContext $context + * @param string $location + * + * @return CriteriaSet + */ + protected function getCriteriaSet(ProfileContext $context, $location) + { + $criteriaSet = new CriteriaSet([ + new DescriptorTypeCriteria( + ProfileContext::ROLE_IDP === $context->getOwnRole() + ? IdpSsoDescriptor::class + : SpSsoDescriptor::class + ), + new LocationCriteria($location), + ]); + + return $criteriaSet; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/AssertBindingTypeAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/AssertBindingTypeAction.php new file mode 100644 index 0000000000..108c4cb662 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/AssertBindingTypeAction.php @@ -0,0 +1,55 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Error\LightSamlContextException; +use Psr\Log\LoggerInterface; + +class AssertBindingTypeAction extends AbstractProfileAction +{ + /** @var string[] */ + protected $expectedBindingTypes; + + /** + * @param LoggerInterface $logger + * @param string[] $expectedBindingTypes + */ + public function __construct(LoggerInterface $logger, array $expectedBindingTypes) + { + parent::__construct($logger); + + $this->expectedBindingTypes = $expectedBindingTypes; + } + + /** + * @param ProfileContext $context + */ + protected function doExecute(ProfileContext $context) + { + if (false === in_array($context->getInboundContext()->getBindingType(), $this->expectedBindingTypes)) { + $message = sprintf( + 'Unexpected binding type "%s" - expected binding types are: %s', + $context->getInboundContext()->getBindingType(), + implode(' ', $this->expectedBindingTypes) + ); + $this->logger->critical($message, LogHelper::getActionErrorContext($context, $this, array( + 'actualBindingType' => $context->getInboundContext()->getBindingType(), + 'expectedBindingTypes' => $this->expectedBindingTypes, + ))); + + throw new LightSamlContextException($context, $message); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/DestinationValidatorAuthnRequestAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/DestinationValidatorAuthnRequestAction.php new file mode 100644 index 0000000000..742d47ec90 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/DestinationValidatorAuthnRequestAction.php @@ -0,0 +1,35 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\Message; + +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Criteria\CriteriaSet; +use LightSaml\Model\Metadata\SingleSignOnService; +use LightSaml\Resolver\Endpoint\Criteria\ServiceTypeCriteria; + +class DestinationValidatorAuthnRequestAction extends AbstractDestinationValidatorAction +{ + /** + * @param ProfileContext $context + * @param string $location + * + * @return CriteriaSet + */ + protected function getCriteriaSet(ProfileContext $context, $location) + { + $result = parent::getCriteriaSet($context, $location); + + $result->add(new ServiceTypeCriteria(SingleSignOnService::class)); + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/DestinationValidatorResponseAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/DestinationValidatorResponseAction.php new file mode 100644 index 0000000000..20c5a38c3a --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/DestinationValidatorResponseAction.php @@ -0,0 +1,35 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\Message; + +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Criteria\CriteriaSet; +use LightSaml\Model\Metadata\AssertionConsumerService; +use LightSaml\Resolver\Endpoint\Criteria\ServiceTypeCriteria; + +class DestinationValidatorResponseAction extends AbstractDestinationValidatorAction +{ + /** + * @param ProfileContext $context + * @param string $location + * + * @return CriteriaSet + */ + protected function getCriteriaSet(ProfileContext $context, $location) + { + $result = parent::getCriteriaSet($context, $location); + + $result->add(new ServiceTypeCriteria(AssertionConsumerService::class)); + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/EntityIdFromMessageIssuerAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/EntityIdFromMessageIssuerAction.php new file mode 100644 index 0000000000..e421d6a285 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/EntityIdFromMessageIssuerAction.php @@ -0,0 +1,30 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Error\LightSamlContextException; + +class EntityIdFromMessageIssuerAction extends AbstractProfileAction +{ + protected function doExecute(ProfileContext $context) + { + $message = MessageContextHelper::asSamlMessage($context->getInboundContext()); + if (null == $message->getIssuer()) { + throw new LightSamlContextException($context, 'Inbound messages does not have Issuer'); + } + + $context->getPartyEntityContext()->setEntityId($message->getIssuer()->getValue()); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/IssuerValidatorAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/IssuerValidatorAction.php new file mode 100644 index 0000000000..225b3390dd --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/IssuerValidatorAction.php @@ -0,0 +1,79 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Error\LightSamlContextException; +use LightSaml\Error\LightSamlValidationException; +use LightSaml\Validator\Model\NameId\NameIdValidatorInterface; +use Psr\Log\LoggerInterface; + +class IssuerValidatorAction extends AbstractProfileAction +{ + /** @var NameIdValidatorInterface */ + protected $nameIdValidator; + + /** @var string */ + protected $allowedFormat; + + /** + * @param LoggerInterface $logger + * @param NameIdValidatorInterface $nameIdValidator + * @param string $allowedFormat + */ + public function __construct(LoggerInterface $logger, NameIdValidatorInterface $nameIdValidator, $allowedFormat) + { + parent::__construct($logger); + + $this->nameIdValidator = $nameIdValidator; + $this->allowedFormat = $allowedFormat; + } + + /** + * @param ProfileContext $context + * + * @return void + */ + protected function doExecute(ProfileContext $context) + { + $message = MessageContextHelper::asSamlMessage($context->getInboundContext()); + + if (false == $message->getIssuer()) { + $message = 'Inbound message must have Issuer element'; + $this->logger->emergency($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + if ($this->allowedFormat && + $message->getIssuer()->getValue() && + $message->getIssuer()->getFormat() && + $message->getIssuer()->getFormat() != $this->allowedFormat + ) { + $message = sprintf( + "Response Issuer Format if set must have value '%s' but it was '%s'", + $this->allowedFormat, + $message->getIssuer()->getFormat() + ); + $this->logger->emergency($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + try { + $this->nameIdValidator->validateNameId($message->getIssuer()); + } catch (LightSamlValidationException $ex) { + throw new LightSamlContextException($context, $ex->getMessage(), 0, $ex); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/MessageSignatureValidatorAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/MessageSignatureValidatorAction.php new file mode 100644 index 0000000000..03c882d183 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/MessageSignatureValidatorAction.php @@ -0,0 +1,82 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Error\LightSamlModelException; +use LightSaml\Model\XmlDSig\AbstractSignatureReader; +use LightSaml\Credential\Criteria\MetadataCriteria; +use LightSaml\Validator\Model\Signature\SignatureValidatorInterface; +use Psr\Log\LoggerInterface; + +/** + * Validates the signature, if any, of the inbound message. + */ +class MessageSignatureValidatorAction extends AbstractProfileAction +{ + /** @var SignatureValidatorInterface */ + protected $signatureValidator; + + /** + * @param LoggerInterface $logger + * @param SignatureValidatorInterface $signatureValidator + */ + public function __construct(LoggerInterface $logger, SignatureValidatorInterface $signatureValidator) + { + parent::__construct($logger); + + $this->signatureValidator = $signatureValidator; + } + + /** + * @param ProfileContext $context + * + * @return void + */ + protected function doExecute(ProfileContext $context) + { + $message = MessageContextHelper::asSamlMessage($context->getInboundContext()); + + $signature = $message->getSignature(); + if (null === $signature) { + $this->logger->debug('Message is not signed', LogHelper::getActionContext($context, $this)); + + return; + } + + if ($signature instanceof AbstractSignatureReader) { + $metadataType = ProfileContext::ROLE_IDP === $context->getOwnRole() ? MetadataCriteria::TYPE_SP : MetadataCriteria::TYPE_IDP; + $credential = $this->signatureValidator->validate($signature, $message->getIssuer()->getValue(), $metadataType); + if ($credential) { + $keyNames = $credential->getKeyNames(); + $this->logger->debug( + sprintf('Message signature validated with key "%s"', implode(', ', $keyNames)), + LogHelper::getActionContext($context, $this, array( + 'credential' => $credential, + )) + ); + } else { + $this->logger->warning( + 'Signature verification was not performed', + LogHelper::getActionContext($context, $this) + ); + } + } else { + $message = 'Expected AbstractSignatureReader'; + $this->logger->critical($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlModelException($message); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/ReceiveMessageAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/ReceiveMessageAction.php new file mode 100644 index 0000000000..517df6c55f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/ReceiveMessageAction.php @@ -0,0 +1,68 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Binding\BindingFactoryInterface; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Error\LightSamlBindingException; +use Psr\Log\LoggerInterface; + +/** + * Receives message from HTTP Request into inbound context, + * optionally enforces biding type to the one specified in the inbound context. + */ +class ReceiveMessageAction extends AbstractProfileAction +{ + /** @var BindingFactoryInterface */ + protected $bindingFactory; + + /** + * @param LoggerInterface $logger + * @param BindingFactoryInterface $bindingFactory + */ + public function __construct(LoggerInterface $logger, BindingFactoryInterface $bindingFactory) + { + parent::__construct($logger); + + $this->bindingFactory = $bindingFactory; + } + + /** + * @param ProfileContext $context + * + * @return void + */ + protected function doExecute(ProfileContext $context) + { + $bindingType = $this->bindingFactory->detectBindingType($context->getHttpRequest()); + if (null == $bindingType) { + $message = 'Unable to resolve binding type, invalid or unsupported http request'; + $this->logger->critical($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlBindingException($message); + } + + $this->logger->debug(sprintf('Detected binding type: %s', $bindingType), LogHelper::getActionContext($context, $this)); + + $binding = $this->bindingFactory->create($bindingType); + $binding->receive($context->getHttpRequest(), $context->getInboundContext()); + $context->getInboundContext()->setBindingType($bindingType); + + $this->logger->info( + 'Received message', + LogHelper::getActionContext($context, $this, array( + 'message' => $context->getInboundContext()->getDeserializationContext()->getDocument()->saveXML(), + )) + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/ResolvePartyEntityIdAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/ResolvePartyEntityIdAction.php new file mode 100644 index 0000000000..e21570e1bc --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Message/ResolvePartyEntityIdAction.php @@ -0,0 +1,129 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Error\LightSamlContextException; +use LightSaml\Meta\TrustOptions\TrustOptions; +use LightSaml\Store\EntityDescriptor\EntityDescriptorStoreInterface; +use LightSaml\Store\TrustOptions\TrustOptionsStoreInterface; +use Psr\Log\LoggerInterface; + +/** + * Looks up inbound message Issuer in entity descriptor providers and sets it to the party context. + */ +class ResolvePartyEntityIdAction extends AbstractProfileAction +{ + /** @var EntityDescriptorStoreInterface */ + private $spEntityDescriptorProvider; + + /** @var EntityDescriptorStoreInterface */ + private $idpEntityDescriptorProvider; + + /** @var TrustOptionsStoreInterface */ + protected $trustOptionsProvider; + + /** + * @param LoggerInterface $logger + * @param EntityDescriptorStoreInterface $spEntityDescriptorProvider + * @param EntityDescriptorStoreInterface $idpEntityDescriptorProvider + * @param TrustOptionsStoreInterface $trustOptionsProvider + */ + public function __construct( + LoggerInterface $logger, + EntityDescriptorStoreInterface $spEntityDescriptorProvider, + EntityDescriptorStoreInterface $idpEntityDescriptorProvider, + TrustOptionsStoreInterface $trustOptionsProvider + ) { + parent::__construct($logger); + + $this->spEntityDescriptorProvider = $spEntityDescriptorProvider; + $this->idpEntityDescriptorProvider = $idpEntityDescriptorProvider; + $this->trustOptionsProvider = $trustOptionsProvider; + } + + /** + * @param ProfileContext $context + */ + protected function doExecute(ProfileContext $context) + { + $partyContext = $context->getPartyEntityContext(); + + if ($partyContext->getEntityDescriptor() && $partyContext->getTrustOptions()) { + $this->logger->debug( + sprintf('Party EntityDescriptor and TrustOptions already set for "%s"', $partyContext->getEntityDescriptor()->getEntityID()), + LogHelper::getActionContext($context, $this, array( + 'partyEntityId' => $partyContext->getEntityDescriptor()->getEntityID(), + )) + ); + + return; + } + + $entityId = $partyContext->getEntityDescriptor() ? $partyContext->getEntityDescriptor()->getEntityID() : null; + $entityId = $entityId ? $entityId : $partyContext->getEntityId(); + if (null == $entityId) { + $message = 'EntityID is not set in the party context'; + $this->logger->critical($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + if (null == $partyContext->getEntityDescriptor()) { + $partyEntityDescriptor = $this->getPartyEntityDescriptor( + $context, + ProfileContext::ROLE_IDP === $context->getOwnRole() + ? $this->spEntityDescriptorProvider + : $this->idpEntityDescriptorProvider, + $context->getPartyEntityContext()->getEntityId() + ); + $partyContext->setEntityDescriptor($partyEntityDescriptor); + $this->logger->debug( + sprintf('Known issuer resolved: "%s"', $partyEntityDescriptor->getEntityID()), + LogHelper::getActionContext($context, $this, array( + 'partyEntityId' => $partyEntityDescriptor->getEntityID(), + )) + ); + } + + if (null == $partyContext->getTrustOptions()) { + $trustOptions = $this->trustOptionsProvider->get($partyContext->getEntityDescriptor()->getEntityID()); + if (null === $trustOptions) { + $trustOptions = new TrustOptions(); + } + $partyContext->setTrustOptions($trustOptions); + } + } + + /** + * @param ProfileContext $context + * @param EntityDescriptorStoreInterface $entityDescriptorProvider + * @param string $entityId + * + * @return \LightSaml\Model\Metadata\EntityDescriptor + */ + protected function getPartyEntityDescriptor( + ProfileContext $context, + EntityDescriptorStoreInterface $entityDescriptorProvider, + $entityId + ) { + $partyEntityDescriptor = $entityDescriptorProvider->get($entityId); + if (null === $partyEntityDescriptor) { + $message = sprintf("Unknown issuer '%s'", $entityId); + $this->logger->emergency($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + return $partyEntityDescriptor; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/AssertionAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/AssertionAction.php new file mode 100644 index 0000000000..3f91539ac6 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/AssertionAction.php @@ -0,0 +1,78 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\Response; + +use LightSaml\Action\ActionInterface; +use LightSaml\Action\DebugPrintTreeActionInterface; +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\AssertionContext; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use Psr\Log\LoggerInterface; + +class AssertionAction extends AbstractProfileAction implements DebugPrintTreeActionInterface +{ + /** @var ActionInterface */ + private $assertionAction; + + /** + * @param LoggerInterface $logger + * @param ActionInterface $assertionAction + */ + public function __construct(LoggerInterface $logger, ActionInterface $assertionAction) + { + parent::__construct($logger); + + $this->assertionAction = $assertionAction; + } + + /** + * @param ProfileContext $context + */ + protected function doExecute(ProfileContext $context) + { + $response = MessageContextHelper::asResponse($context->getInboundContext()); + + foreach ($response->getAllAssertions() as $index => $assertion) { + $name = sprintf('assertion_%s', $index); + /** @var AssertionContext $assertionContext */ + $assertionContext = $context->getSubContext($name, AssertionContext::class); + $assertionContext + ->setAssertion($assertion) + ->setId($name) + ; + + $this->assertionAction->execute($assertionContext); + } + } + + /** + * @param int $depth + * + * @return array + */ + public function debugPrintTree($depth = 0) + { + $arr = array(); + if ($this->assertionAction instanceof DebugPrintTreeActionInterface) { + $arr = array_merge($arr, $this->assertionAction->debugPrintTree()); + } else { + $arr[get_class($this->assertionAction)] = array(); + } + + $result = array( + static::class => $arr, + ); + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/DecryptAssertionsAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/DecryptAssertionsAction.php new file mode 100644 index 0000000000..ae707dc9f5 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/DecryptAssertionsAction.php @@ -0,0 +1,106 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\Response; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Error\LightSamlContextException; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Model\Assertion\EncryptedAssertionReader; +use LightSaml\Resolver\Credential\CredentialResolverInterface; +use LightSaml\SamlConstants; +use LightSaml\Credential\CredentialInterface; +use LightSaml\Credential\UsageType; +use LightSaml\Credential\Criteria\EntityIdCriteria; +use LightSaml\Credential\Criteria\MetadataCriteria; +use LightSaml\Credential\Criteria\UsageCriteria; +use Psr\Log\LoggerInterface; + +class DecryptAssertionsAction extends AbstractProfileAction +{ + /** @var CredentialResolverInterface */ + protected $credentialResolver; + + /** + * @param LoggerInterface $logger + * @param CredentialResolverInterface $credentialResolver + */ + public function __construct(LoggerInterface $logger, CredentialResolverInterface $credentialResolver) + { + parent::__construct($logger); + + $this->credentialResolver = $credentialResolver; + } + + /** + * @param ProfileContext $context + */ + protected function doExecute(ProfileContext $context) + { + $response = MessageContextHelper::asResponse($context->getInboundContext()); + + if (0 === count($response->getAllEncryptedAssertions())) { + $this->logger->debug('Response has no encrypted assertions', LogHelper::getActionContext($context, $this)); + + return; + } + + $ownEntityDescriptor = $context->getOwnEntityDescriptor(); + + $query = $this->credentialResolver->query(); + $query + ->add(new EntityIdCriteria($ownEntityDescriptor->getEntityID())) + ->add(new MetadataCriteria( + ProfileContext::ROLE_IDP === $context->getOwnRole() + ? MetadataCriteria::TYPE_IDP + : MetadataCriteria::TYPE_SP, + SamlConstants::PROTOCOL_SAML2 + )) + ->add(new UsageCriteria(UsageType::ENCRYPTION)) + ; + $query->resolve(); + $privateKeys = $query->getPrivateKeys(); + if (empty($privateKeys)) { + $message = 'No credentials resolved for assertion decryption'; + $this->logger->emergency($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + $this->logger->info('Trusted decryption candidates', LogHelper::getActionContext($context, $this, array( + 'credentials' => array_map(function (CredentialInterface $credential) { + return sprintf( + "Entity: '%s'; PK X509 Thumb: '%s'", + $credential->getEntityId(), + $credential->getPublicKey() ? $credential->getPublicKey()->getX509Thumbprint() : '' + ); + }, $privateKeys), + ))); + + foreach ($response->getAllEncryptedAssertions() as $index => $encryptedAssertion) { + if ($encryptedAssertion instanceof EncryptedAssertionReader) { + $name = sprintf('assertion_encrypted_%s', $index); + /** @var DeserializationContext $deserializationContext */ + $deserializationContext = $context->getInboundContext()->getSubContext($name, DeserializationContext::class); + $assertion = $encryptedAssertion->decryptMultiAssertion($privateKeys, $deserializationContext); + $response->addAssertion($assertion); + + $this->logger->info( + 'Assertion decrypted', + LogHelper::getActionContext($context, $this, array( + 'assertion' => $deserializationContext->getDocument()->saveXML(), + )) + ); + } + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/HasAssertionsValidatorAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/HasAssertionsValidatorAction.php new file mode 100644 index 0000000000..349a93ab74 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/HasAssertionsValidatorAction.php @@ -0,0 +1,34 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\Response; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Error\LightSamlContextException; + +class HasAssertionsValidatorAction extends AbstractProfileAction +{ + protected function doExecute(ProfileContext $context) + { + $response = MessageContextHelper::asResponse($context->getInboundContext()); + + if ($response->getAllAssertions()) { + return; + } + + $message = 'Response must contain at least one assertion'; + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/HasAuthnStatementValidatorAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/HasAuthnStatementValidatorAction.php new file mode 100644 index 0000000000..43e567c4f0 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/HasAuthnStatementValidatorAction.php @@ -0,0 +1,36 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\Response; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Error\LightSamlContextException; + +class HasAuthnStatementValidatorAction extends AbstractProfileAction +{ + protected function doExecute(ProfileContext $context) + { + $response = MessageContextHelper::asResponse($context->getInboundContext()); + + foreach ($response->getAllAssertions() as $assertion) { + if ($assertion->getAllAuthnStatements()) { + return; + } + } + + $message = 'Response must have at least one Assertion containing AuthnStatement element'; + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/HasBearerAssertionsValidatorAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/HasBearerAssertionsValidatorAction.php new file mode 100644 index 0000000000..db338c92c2 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/HasBearerAssertionsValidatorAction.php @@ -0,0 +1,34 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\Response; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Error\LightSamlContextException; + +class HasBearerAssertionsValidatorAction extends AbstractProfileAction +{ + protected function doExecute(ProfileContext $context) + { + $response = MessageContextHelper::asResponse($context->getInboundContext()); + + if ($response->getBearerAssertions()) { + return; + } + + $message = 'Response must contain at least one bearer assertion'; + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/SpSsoStateAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/SpSsoStateAction.php new file mode 100644 index 0000000000..edd2b4e2fb --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/Response/SpSsoStateAction.php @@ -0,0 +1,45 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\Response; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Resolver\Session\SessionProcessorInterface; +use Psr\Log\LoggerInterface; + +class SpSsoStateAction extends AbstractProfileAction +{ + /** @var SessionProcessorInterface */ + private $sessionProcessor; + + public function __construct(LoggerInterface $logger, SessionProcessorInterface $sessionProcessor) + { + parent::__construct($logger); + + $this->sessionProcessor = $sessionProcessor; + } + + /** + * @param ProfileContext $context + */ + protected function doExecute(ProfileContext $context) + { + $response = MessageContextHelper::asResponse($context->getInboundContext()); + + $this->sessionProcessor->processAssertions( + $response->getAllAssertions(), + $context->getOwnEntityDescriptor()->getEntityID(), + $context->getPartyEntityDescriptor()->getEntityID() + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/StatusResponse/InResponseToValidatorAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/StatusResponse/InResponseToValidatorAction.php new file mode 100644 index 0000000000..2bd7ef2d45 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/StatusResponse/InResponseToValidatorAction.php @@ -0,0 +1,72 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\StatusResponse; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\ProfileContexts; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Context\Profile\RequestStateContext; +use LightSaml\Error\LightSamlContextException; +use LightSaml\State\Request\RequestStateParameters; +use LightSaml\Store\Request\RequestStateStoreInterface; +use Psr\Log\LoggerInterface; + +class InResponseToValidatorAction extends AbstractProfileAction +{ + /** @var RequestStateStoreInterface */ + protected $requestStore; + + /** + * @param LoggerInterface $logger + * @param RequestStateStoreInterface $requestStore + */ + public function __construct(LoggerInterface $logger, RequestStateStoreInterface $requestStore) + { + parent::__construct($logger); + + $this->requestStore = $requestStore; + } + + /** + * @param ProfileContext $context + */ + protected function doExecute(ProfileContext $context) + { + $response = MessageContextHelper::asStatusResponse($context->getInboundContext()); + $inResponseTo = $response->getInResponseTo(); + if ($inResponseTo) { + $requestState = $this->requestStore->get($inResponseTo); + if (null == $requestState) { + $message = sprintf("Unknown InResponseTo '%s'", $inResponseTo); + $this->logger->critical($message, LogHelper::getActionErrorContext($context, $this, array( + 'in_response_to' => $inResponseTo, + ))); + throw new LightSamlContextException($context, $message); + } + $sentToParty = $requestState->getParameters()->get(RequestStateParameters::PARTY); + if ($sentToParty && $response->getIssuer() && $response->getIssuer()->getValue() != $sentToParty) { + $message = sprintf('AuthnRequest with id "%s" sent to party "%s" but StatusResponse for that request issued by party "%s"', $inResponseTo, $sentToParty, $response->getIssuer()->getValue()); + $this->logger->critical($message, LogHelper::getActionErrorContext($context, $this, array( + 'sent_to' => $sentToParty, + 'received_from' => $response->getIssuer()->getValue(), + ))); + throw new LightSamlContextException($context, $message); + } + + /** @var RequestStateContext $requestStateContext */ + $requestStateContext = $context->getInboundContext()->getSubContext(ProfileContexts::REQUEST_STATE, RequestStateContext::class); + $requestStateContext->setRequestState($requestState); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/StatusResponse/StatusAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/StatusResponse/StatusAction.php new file mode 100644 index 0000000000..9271a53956 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Inbound/StatusResponse/StatusAction.php @@ -0,0 +1,50 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Inbound\StatusResponse; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Error\LightSamlAuthenticationException; +use LightSaml\Error\LightSamlContextException; + +/** + * Throws LightSamlAuthenticationException if status of inbound message is not successful. + */ +class StatusAction extends AbstractProfileAction +{ + protected function doExecute(ProfileContext $context) + { + $statusResponse = MessageContextHelper::asStatusResponse($context->getInboundContext()); + + if ($statusResponse->getStatus() && $statusResponse->getStatus()->isSuccess()) { + return; + } + + if (null == $statusResponse->getStatus()) { + $message = 'Status response does not have Status set'; + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + $status = $statusResponse->getStatus()->getStatusCode()->getValue(); + $status .= "\n".$statusResponse->getStatus()->getStatusMessage(); + if ($statusResponse->getStatus()->getStatusCode()->getStatusCode()) { + $status .= "\n".$statusResponse->getStatus()->getStatusCode()->getStatusCode()->getValue(); + } + + $message = 'Unsuccessful SAML response: '.$status; + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this, ['status' => $status])); + throw new LightSamlAuthenticationException($statusResponse, $message); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/AuthnRequest/ACSUrlAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/AuthnRequest/ACSUrlAction.php new file mode 100644 index 0000000000..7d3af17359 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/AuthnRequest/ACSUrlAction.php @@ -0,0 +1,62 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Outbound\AuthnRequest; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Criteria\CriteriaSet; +use LightSaml\Error\LightSamlContextException; +use LightSaml\Model\Metadata\AssertionConsumerService; +use LightSaml\Model\Metadata\SpSsoDescriptor; +use LightSaml\Resolver\Endpoint\Criteria\BindingCriteria; +use LightSaml\Resolver\Endpoint\Criteria\DescriptorTypeCriteria; +use LightSaml\Resolver\Endpoint\Criteria\ServiceTypeCriteria; +use LightSaml\Resolver\Endpoint\EndpointResolverInterface; +use LightSaml\SamlConstants; +use Psr\Log\LoggerInterface; + +// TODO ACSUrlAction not used in profile builder, has to be added +class ACSUrlAction extends AbstractProfileAction +{ + /** @var EndpointResolverInterface */ + private $endpointResolver; + + public function __construct(LoggerInterface $logger, EndpointResolverInterface $endpointResolver) + { + parent::__construct($logger); + + $this->endpointResolver = $endpointResolver; + } + + protected function doExecute(ProfileContext $context) + { + $ownEntityDescriptor = $context->getOwnEntityDescriptor(); + + $criteriaSet = new CriteriaSet([ + new DescriptorTypeCriteria(SpSsoDescriptor::class), + new ServiceTypeCriteria(AssertionConsumerService::class), + new BindingCriteria([SamlConstants::BINDING_SAML2_HTTP_POST]), + ]); + + $endpoints = $this->endpointResolver->resolve($criteriaSet, $ownEntityDescriptor->getAllEndpoints()); + if (empty($endpoints)) { + $message = 'Missing ACS Service with HTTP POST binding in own SP SSO Descriptor'; + $this->logger->error($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + MessageContextHelper::asAuthnRequest($context->getOutboundContext()) + ->setAssertionConsumerServiceURL($endpoints[0]->getEndpoint()->getLocation()); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/AuthnRequest/CreateAuthnRequestAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/AuthnRequest/CreateAuthnRequestAction.php new file mode 100644 index 0000000000..f5512ab33e --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/AuthnRequest/CreateAuthnRequestAction.php @@ -0,0 +1,27 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Outbound\AuthnRequest; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Model\Protocol\AuthnRequest; + +/** + * Creates empty AuthnRequest in outbound context. + */ +class CreateAuthnRequestAction extends AbstractProfileAction +{ + protected function doExecute(ProfileContext $context) + { + $context->getOutboundContext()->setMessage(new AuthnRequest()); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/CreateMessageIssuerAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/CreateMessageIssuerAction.php new file mode 100644 index 0000000000..6a720978ff --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/CreateMessageIssuerAction.php @@ -0,0 +1,46 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Outbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Model\Assertion\Issuer; +use LightSaml\SamlConstants; + +/** + * Sets the Issuer of the outbound message to the value of own entityID. + */ +class CreateMessageIssuerAction extends AbstractProfileAction +{ + /** + * @param ProfileContext $context + * + * @return void + */ + protected function doExecute(ProfileContext $context) + { + $ownEntityDescriptor = $context->getOwnEntityDescriptor(); + + $issuer = new Issuer($ownEntityDescriptor->getEntityID()); + $issuer->setFormat(SamlConstants::NAME_ID_FORMAT_ENTITY); + + MessageContextHelper::asSamlMessage($context->getOutboundContext()) + ->setIssuer($issuer); + + $this->logger->debug( + sprintf('Issuer set to "%s"', $ownEntityDescriptor->getEntityID()), + LogHelper::getActionContext($context, $this) + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/DestinationAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/DestinationAction.php new file mode 100644 index 0000000000..ad8d8e2532 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/DestinationAction.php @@ -0,0 +1,41 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Outbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; + +/** + * Sets destination of the outbound message to the value of location of endpoint from the context. + */ +class DestinationAction extends AbstractProfileAction +{ + /** + * @param ProfileContext $context + * + * @return void + */ + protected function doExecute(ProfileContext $context) + { + $endpoint = $context->getEndpoint(); + + MessageContextHelper::asSamlMessage($context->getOutboundContext()) + ->setDestination($endpoint->getLocation()); + + $this->logger->debug( + sprintf('Destination set to "%s"', $endpoint->getLocation()), + LogHelper::getActionContext($context, $this) + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/ForwardRelayStateAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/ForwardRelayStateAction.php new file mode 100644 index 0000000000..f0537cb02e --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/ForwardRelayStateAction.php @@ -0,0 +1,32 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Outbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\ProfileContext; + +class ForwardRelayStateAction extends AbstractProfileAction +{ + protected function doExecute(ProfileContext $context) + { + if (null == $context->getInboundContext()->getMessage()) { + return; + } + + if ($context->getInboundMessage()->getRelayState()) { + $this->logger->debug(sprintf('Forwarding relay state from inbound message: "%s"', $context->getInboundMessage()->getRelayState())); + $context->getOutboundMessage()->setRelayState( + $context->getInboundMessage()->getRelayState() + ); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/MessageIdAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/MessageIdAction.php new file mode 100644 index 0000000000..b1bde16af7 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/MessageIdAction.php @@ -0,0 +1,36 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Outbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Helper; + +/** + * Sets the ID of the message in the outbound context. + */ +class MessageIdAction extends AbstractProfileAction +{ + protected function doExecute(ProfileContext $context) + { + $id = Helper::generateID(); + MessageContextHelper::asSamlMessage($context->getOutboundContext()) + ->setId($id); + + $this->logger->info( + sprintf('Message ID set to "%s"', $id), + LogHelper::getActionContext($context, $this, array('message_id' => $id)) + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/MessageIssueInstantAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/MessageIssueInstantAction.php new file mode 100644 index 0000000000..4062bfd200 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/MessageIssueInstantAction.php @@ -0,0 +1,55 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Outbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Provider\TimeProvider\TimeProviderInterface; +use Psr\Log\LoggerInterface; + +/** + * Sets outbound message IssueInstant to the value provided by given time provider. + */ +class MessageIssueInstantAction extends AbstractProfileAction +{ + /** @var TimeProviderInterface */ + protected $timeProvider; + + /** + * @param LoggerInterface $logger + * @param TimeProviderInterface $timeProvider + */ + public function __construct(LoggerInterface $logger, TimeProviderInterface $timeProvider) + { + parent::__construct($logger); + + $this->timeProvider = $timeProvider; + } + + /** + * @param ProfileContext $context + * + * @return void + */ + protected function doExecute(ProfileContext $context) + { + MessageContextHelper::asSamlMessage($context->getOutboundContext()) + ->setIssueInstant($this->timeProvider->getTimestamp()); + + $this->logger->info( + sprintf('Message IssueInstant set to "%s"', MessageContextHelper::asSamlMessage($context->getOutboundContext())->getIssueInstantString()), + LogHelper::getActionContext($context, $this) + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/MessageVersionAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/MessageVersionAction.php new file mode 100644 index 0000000000..4341378897 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/MessageVersionAction.php @@ -0,0 +1,54 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Outbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use Psr\Log\LoggerInterface; + +/** + * Sets the Version of the outbound message to the given value. + */ +class MessageVersionAction extends AbstractProfileAction +{ + /** @var string */ + private $version; + + /** + * @param LoggerInterface $logger + * @param string $version + */ + public function __construct(LoggerInterface $logger, $version) + { + parent::__construct($logger); + + $this->version = $version; + } + + /** + * @param ProfileContext $context + * + * @return void + */ + protected function doExecute(ProfileContext $context) + { + MessageContextHelper::asSamlMessage($context->getOutboundContext()) + ->setVersion($this->version); + + $this->logger->debug( + sprintf('Message Version set to "%s"', $this->version), + LogHelper::getActionContext($context, $this) + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/ResolveEndpointBaseAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/ResolveEndpointBaseAction.php new file mode 100644 index 0000000000..122623772f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/ResolveEndpointBaseAction.php @@ -0,0 +1,173 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Outbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Error\LightSamlContextException; +use LightSaml\Model\Metadata\EndpointReference; +use LightSaml\Model\Metadata\IdpSsoDescriptor; +use LightSaml\Model\Metadata\SpSsoDescriptor; +use LightSaml\Model\Protocol\AuthnRequest; +use LightSaml\Resolver\Endpoint\Criteria\BindingCriteria; +use LightSaml\Resolver\Endpoint\Criteria\DescriptorTypeCriteria; +use LightSaml\Resolver\Endpoint\Criteria\IndexCriteria; +use LightSaml\Resolver\Endpoint\Criteria\LocationCriteria; +use LightSaml\Resolver\Endpoint\Criteria\ServiceTypeCriteria; +use LightSaml\Resolver\Endpoint\EndpointResolverInterface; +use LightSaml\Criteria\CriteriaSet; +use LightSaml\SamlConstants; +use Psr\Log\LoggerInterface; + +/** + * Determines to which endpoint outbound message will be sent. + */ +abstract class ResolveEndpointBaseAction extends AbstractProfileAction +{ + /** @var EndpointResolverInterface */ + protected $endpointResolver; + + /** + * @param LoggerInterface $logger + * @param EndpointResolverInterface $endpointResolver + */ + public function __construct(LoggerInterface $logger, EndpointResolverInterface $endpointResolver) + { + parent::__construct($logger); + + $this->endpointResolver = $endpointResolver; + } + + /** + * @param ProfileContext $context + */ + protected function doExecute(ProfileContext $context) + { + if ($context->getEndpointContext()->getEndpoint()) { + $this->logger->debug( + sprintf( + 'Endpoint already set with location "%s" and binding "%s"', + $context->getEndpoint()->getLocation(), + $context->getEndpoint()->getBinding() + ), + LogHelper::getActionContext($context, $this, array( + 'endpointLocation' => $context->getEndpoint()->getLocation(), + 'endpointBinding' => $context->getEndpoint()->getBinding(), + )) + ); + + return; + } + + $criteriaSet = $this->getCriteriaSet($context); + + $message = $context->getInboundContext()->getMessage(); + if ($message instanceof AuthnRequest) { + if (null !== $message->getAssertionConsumerServiceIndex()) { + $criteriaSet->add(new IndexCriteria($message->getAssertionConsumerServiceIndex())); + } + if (null !== $message->getAssertionConsumerServiceURL()) { + $criteriaSet->add(new LocationCriteria($message->getAssertionConsumerServiceURL())); + } + } + + $candidates = $this->endpointResolver->resolve($criteriaSet, $context->getPartyEntityDescriptor()->getAllEndpoints()); + /** @var EndpointReference $endpointReference */ + $endpointReference = array_shift($candidates); + + if (null == $endpointReference) { + $message = sprintf( + "Unable to determine endpoint for entity '%s'", + $context->getPartyEntityDescriptor()->getEntityID() + ); + $this->logger->emergency($message, LogHelper::getActionErrorContext($context, $this)); + throw new LightSamlContextException($context, $message); + } + + $this->logger->debug( + sprintf( + 'Endpoint resolved to location "%s" and binding "%s"', + $endpointReference->getEndpoint()->getLocation(), + $endpointReference->getEndpoint()->getBinding() + ), + LogHelper::getActionContext($context, $this, array( + 'endpointLocation' => $endpointReference->getEndpoint()->getLocation(), + 'endpointBinding' => $endpointReference->getEndpoint()->getBinding(), + )) + ); + + $context->getEndpointContext()->setEndpoint($endpointReference->getEndpoint()); + } + + /** + * @param ProfileContext $context + * + * @return CriteriaSet + */ + protected function getCriteriaSet(ProfileContext $context) + { + $criteriaSet = new CriteriaSet(); + + $bindings = $this->getBindings($context); + if ($bindings) { + $criteriaSet->add(new BindingCriteria($bindings)); + } + + $descriptorType = $this->getDescriptorType($context); + if ($descriptorType) { + $criteriaSet->add(new DescriptorTypeCriteria($descriptorType)); + } + + $serviceType = $this->getServiceType($context); + if ($serviceType) { + $criteriaSet->add(new ServiceTypeCriteria($serviceType)); + } + + return $criteriaSet; + } + + /** + * @param ProfileContext $context + * + * @return string[] + */ + protected function getBindings(ProfileContext $context) + { + return array( + SamlConstants::BINDING_SAML2_HTTP_POST, + SamlConstants::BINDING_SAML2_HTTP_REDIRECT, + ); + } + + /** + * @param ProfileContext $context + * + * @return string|null + */ + protected function getDescriptorType(ProfileContext $context) + { + return ProfileContext::ROLE_IDP == $context->getOwnRole() + ? SpSsoDescriptor::class + : IdpSsoDescriptor::class; + } + + /** + * @param ProfileContext $context + * + * @return string|null + */ + protected function getServiceType(ProfileContext $context) + { + return; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/ResolveEndpointIdpSsoAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/ResolveEndpointIdpSsoAction.php new file mode 100644 index 0000000000..e4807a317c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/ResolveEndpointIdpSsoAction.php @@ -0,0 +1,23 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Outbound\Message; + +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Model\Metadata\SingleSignOnService; + +class ResolveEndpointIdpSsoAction extends ResolveEndpointBaseAction +{ + protected function getServiceType(ProfileContext $context) + { + return SingleSignOnService::class; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/ResolveEndpointSloAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/ResolveEndpointSloAction.php new file mode 100644 index 0000000000..c70f62926e --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/ResolveEndpointSloAction.php @@ -0,0 +1,40 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Outbound\Message; + +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Error\LightSamlContextException; +use LightSaml\Model\Metadata\IdpSsoDescriptor; +use LightSaml\Model\Metadata\SingleLogoutService; +use LightSaml\Model\Metadata\SpSsoDescriptor; + +class ResolveEndpointSloAction extends ResolveEndpointBaseAction +{ + protected function getServiceType(ProfileContext $context) + { + return SingleLogoutService::class; + } + + protected function getDescriptorType(ProfileContext $context) + { + $ssoSessionState = $context->getLogoutSsoSessionState(); + $ownEntityId = $context->getOwnEntityDescriptor()->getEntityID(); + + if ($ssoSessionState->getIdpEntityId() == $ownEntityId) { + return SpSsoDescriptor::class; + } elseif ($ssoSessionState->getSpEntityId() == $ownEntityId) { + return IdpSsoDescriptor::class; + } else { + throw new LightSamlContextException($context, 'Unable to resolve logout target descriptor type'); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/ResolveEndpointSpAcsAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/ResolveEndpointSpAcsAction.php new file mode 100644 index 0000000000..324567b8e8 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/ResolveEndpointSpAcsAction.php @@ -0,0 +1,23 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Outbound\Message; + +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Model\Metadata\AssertionConsumerService; + +class ResolveEndpointSpAcsAction extends ResolveEndpointBaseAction +{ + protected function getServiceType(ProfileContext $context) + { + return AssertionConsumerService::class; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/SaveRequestStateAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/SaveRequestStateAction.php new file mode 100644 index 0000000000..ebf67478ca --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/SaveRequestStateAction.php @@ -0,0 +1,72 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Outbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Model\Protocol\LogoutRequest; +use LightSaml\State\Request\RequestState; +use LightSaml\State\Request\RequestStateParameters; +use LightSaml\Store\Request\RequestStateStoreInterface; +use Psr\Log\LoggerInterface; + +class SaveRequestStateAction extends AbstractProfileAction +{ + /** @var RequestStateStoreInterface */ + protected $requestStore; + + /** + * @param LoggerInterface $logger + * @param RequestStateStoreInterface $requestStore + */ + public function __construct(LoggerInterface $logger, RequestStateStoreInterface $requestStore) + { + parent::__construct($logger); + + $this->requestStore = $requestStore; + } + + /** + * @param ProfileContext $context + */ + protected function doExecute(ProfileContext $context) + { + $message = MessageContextHelper::asSamlMessage($context->getOutboundContext()); + + $state = new RequestState(); + $state->setId($message->getID()); + + $partyEntityId = $context->getPartyEntityContext() ? $context->getPartyEntityContext()->getEntityId() : ''; + if ($context->getPartyEntityContext() && $context->getPartyEntityContext()->getEntityDescriptor()) { + $partyEntityId = $context->getPartyEntityContext()->getEntityDescriptor()->getEntityID(); + } + + $state->getParameters()->add([ + RequestStateParameters::ID => $message->getID(), + RequestStateParameters::TYPE => get_class($message), + RequestStateParameters::TIMESTAMP => $message->getIssueInstantTimestamp(), + RequestStateParameters::PARTY => $partyEntityId, + RequestStateParameters::RELAY_STATE => $message->getRelayState(), + ]); + + if ($message instanceof LogoutRequest) { + $state->getParameters()->add([ + RequestStateParameters::NAME_ID => $message->getNameID()->getValue(), + RequestStateParameters::NAME_ID_FORMAT => $message->getNameID()->getFormat(), + RequestStateParameters::SESSION_INDEX => $message->getSessionIndex(), + ]); + } + + $this->requestStore->set($state); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/SendMessageAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/SendMessageAction.php new file mode 100644 index 0000000000..b424ca1f5c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/SendMessageAction.php @@ -0,0 +1,58 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Outbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Binding\BindingFactoryInterface; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\ProfileContext; +use Psr\Log\LoggerInterface; + +class SendMessageAction extends AbstractProfileAction +{ + /** @var BindingFactoryInterface */ + protected $bindingFactory; + + /** + * @param LoggerInterface $logger + * @param BindingFactoryInterface $bindingFactory + */ + public function __construct(LoggerInterface $logger, BindingFactoryInterface $bindingFactory) + { + parent::__construct($logger); + + $this->bindingFactory = $bindingFactory; + } + + /** + * @param ProfileContext $context + * + * @return void + */ + public function doExecute(ProfileContext $context) + { + $binding = $this->bindingFactory->create($context->getEndpoint()->getBinding()); + + $outboundContext = $context->getOutboundContext(); + + $context->getHttpResponseContext()->setResponse( + $binding->send($outboundContext) + ); + + $this->logger->info( + 'Sending message', + LogHelper::getActionContext($context, $this, array( + 'message' => $outboundContext->getSerializationContext()->getDocument()->saveXML(), + )) + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/SetRelayStateAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/SetRelayStateAction.php new file mode 100644 index 0000000000..0fe25ce146 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/SetRelayStateAction.php @@ -0,0 +1,32 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Outbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; + +class SetRelayStateAction extends AbstractProfileAction +{ + protected function doExecute(ProfileContext $context) + { + if ($context->getRelayState()) { + $this->logger->debug( + sprintf('RelayState from context set to outbound message: "%s"', $context->getRelayState()), + LogHelper::getActionContext($context, $this) + ); + MessageContextHelper::asSamlMessage($context->getOutboundContext()) + ->setRelayState($context->getRelayState()); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/SignMessageAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/SignMessageAction.php new file mode 100644 index 0000000000..e4a762dcba --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/Profile/Outbound/Message/SignMessageAction.php @@ -0,0 +1,93 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action\Profile\Outbound\Message; + +use LightSaml\Action\Profile\AbstractProfileAction; +use LightSaml\Context\Profile\Helper\LogHelper; +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Model\Protocol\AuthnRequest; +use LightSaml\Model\Protocol\LogoutRequest; +use LightSaml\Model\Protocol\Response; +use LightSaml\Resolver\Signature\SignatureResolverInterface; +use Psr\Log\LoggerInterface; + +/** + * Signs the outbound message, according to TrustOptions settings. + */ +class SignMessageAction extends AbstractProfileAction +{ + /** @var SignatureResolverInterface */ + protected $signatureResolver; + + /** + * @param LoggerInterface $logger + * @param SignatureResolverInterface $signatureResolver + */ + public function __construct(LoggerInterface $logger, SignatureResolverInterface $signatureResolver) + { + parent::__construct($logger); + + $this->signatureResolver = $signatureResolver; + } + + protected function doExecute(ProfileContext $context) + { + $shouldSign = $this->shouldSignMessage($context); + if ($shouldSign) { + $signature = $this->signatureResolver->getSignature($context); + if ($signature) { + MessageContextHelper::asSamlMessage($context->getOutboundContext()) + ->setSignature($signature) + ; + + $this->logger->debug( + sprintf('Message signed with fingerprint "%s"', $signature->getCertificate()->getFingerprint()), + LogHelper::getActionContext($context, $this, array( + 'certificate' => $signature->getCertificate()->getInfo(), + )) + ); + } else { + $this->logger->critical( + 'No signature resolved, although signing enabled', + LogHelper::getActionErrorContext($context, $this, array()) + ); + } + } else { + $this->logger->debug('Signing disabled', LogHelper::getActionContext($context, $this)); + } + } + + /** + * @param ProfileContext $context + * + * @return bool + */ + private function shouldSignMessage(ProfileContext $context) + { + $message = $context->getOutboundMessage(); + + if ($message instanceof LogoutRequest) { + return true; + } + + $trustOptions = $context->getTrustOptions(); + + if ($message instanceof AuthnRequest) { + return $trustOptions->getSignAuthnRequest(); + } elseif ($message instanceof Response) { + return $trustOptions->getSignResponse(); + } + + throw new \LogicException(sprintf('Unexpected message type "%s"', get_class($message))); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Action/WrappedAction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Action/WrappedAction.php new file mode 100644 index 0000000000..489c22373c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Action/WrappedAction.php @@ -0,0 +1,52 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Action; + +use LightSaml\Context\ContextInterface; + +abstract class WrappedAction implements ActionInterface +{ + /** + * @var ActionInterface + */ + protected $action; + + /** + * @param ActionInterface $action + */ + public function __construct(ActionInterface $action) + { + $this->action = $action; + } + + /** + * @param ContextInterface $context + * + * @return void + */ + public function execute(ContextInterface $context) + { + $this->beforeAction($context); + $this->action->execute($context); + $this->afterAction($context); + } + + /** + * @param ContextInterface $context + */ + abstract protected function beforeAction(ContextInterface $context); + + /** + * @param ContextInterface $context + */ + abstract protected function afterAction(ContextInterface $context); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Binding/AbstractBinding.php b/vendor/lightsaml/lightsaml/src/LightSaml/Binding/AbstractBinding.php new file mode 100644 index 0000000000..2948b23193 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Binding/AbstractBinding.php @@ -0,0 +1,78 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Binding; + +use LightSaml\Context\Profile\MessageContext; +use LightSaml\Event\Events; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\GenericEvent; +use Symfony\Component\HttpFoundation\Request; + +abstract class AbstractBinding +{ + /** @var EventDispatcherInterface|null */ + protected $eventDispatcher; + + /** + * @param EventDispatcherInterface|null $eventDispatcher + * + * @return AbstractBinding + */ + public function setEventDispatcher(EventDispatcherInterface $eventDispatcher = null) + { + $this->eventDispatcher = $eventDispatcher; + + return $this; + } + + /** + * @return null|\Symfony\Component\EventDispatcher\EventDispatcherInterface + */ + public function getEventDispatcher() + { + return $this->eventDispatcher; + } + + /** + * @param string $messageString + */ + protected function dispatchReceive($messageString) + { + if ($this->eventDispatcher) { + $this->eventDispatcher->dispatch(Events::BINDING_MESSAGE_RECEIVED, new GenericEvent($messageString)); + } + } + + /** + * @param string $messageString + */ + protected function dispatchSend($messageString) + { + if ($this->eventDispatcher) { + $this->eventDispatcher->dispatch(Events::BINDING_MESSAGE_SENT, new GenericEvent($messageString)); + } + } + + /** + * @param MessageContext $context + * @param null|string $destination + * + * @return \Symfony\Component\HttpFoundation\Response + */ + abstract public function send(MessageContext $context, $destination = null); + + /** + * @param Request $request + * @param MessageContext $context + */ + abstract public function receive(Request $request, MessageContext $context); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Binding/BindingFactory.php b/vendor/lightsaml/lightsaml/src/LightSaml/Binding/BindingFactory.php new file mode 100644 index 0000000000..02e0c306ed --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Binding/BindingFactory.php @@ -0,0 +1,152 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Binding; + +use LightSaml\Error\LightSamlBindingException; +use LightSaml\SamlConstants; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; + +class BindingFactory implements BindingFactoryInterface +{ + /** @var EventDispatcherInterface|null */ + protected $eventDispatcher; + + /** + * @param EventDispatcherInterface $eventDispatcher + */ + public function __construct(EventDispatcherInterface $eventDispatcher = null) + { + $this->eventDispatcher = $eventDispatcher; + } + + /** + * @param null|EventDispatcherInterface $eventDispatcher + * + * @return BindingFactoryInterface + */ + public function setEventDispatcher(EventDispatcherInterface $eventDispatcher = null) + { + $this->eventDispatcher = $eventDispatcher; + + return $this; + } + + /** + * @param Request $request + * + * @return AbstractBinding + */ + public function getBindingByRequest(Request $request) + { + $bindingType = $this->detectBindingType($request); + + return $this->create($bindingType); + } + + /** + * @param string $bindingType + * + * @throws \LogicException + * @throws \LightSaml\Error\LightSamlBindingException + * + * @return AbstractBinding + */ + public function create($bindingType) + { + $result = null; + switch ($bindingType) { + case SamlConstants::BINDING_SAML2_HTTP_REDIRECT: + $result = new HttpRedirectBinding(); + break; + + case SamlConstants::BINDING_SAML2_HTTP_POST: + $result = new HttpPostBinding(); + break; + + case SamlConstants::BINDING_SAML2_HTTP_ARTIFACT: + throw new \LogicException('Artifact binding not implemented'); + case SamlConstants::BINDING_SAML2_SOAP: + throw new \LogicException('SOAP binding not implemented'); + } + + if ($result) { + $result->setEventDispatcher($this->eventDispatcher); + + return $result; + } + + throw new LightSamlBindingException(sprintf("Unknown binding type '%s'", $bindingType)); + } + + /** + * @param Request $request + * + * @return string|null + */ + public function detectBindingType(Request $request) + { + $requestMethod = trim(strtoupper($request->getMethod())); + if ('GET' == $requestMethod) { + return $this->processGET($request); + } elseif ('POST' == $requestMethod) { + return $this->processPOST($request); + } + + return null; + } + + /** + * @param Request $request + * + * @return null|string + */ + protected function processGET(Request $request) + { + $get = $request->query->all(); + if (array_key_exists('SAMLRequest', $get) || array_key_exists('SAMLResponse', $get)) { + return SamlConstants::BINDING_SAML2_HTTP_REDIRECT; + } elseif (array_key_exists('SAMLart', $get)) { + return SamlConstants::BINDING_SAML2_HTTP_ARTIFACT; + } + + return null; + } + + /** + * @param Request $request + * + * @return null|string + */ + protected function processPOST(Request $request) + { + $post = $request->request->all(); + if (array_key_exists('SAMLRequest', $post) || array_key_exists('SAMLResponse', $post)) { + return SamlConstants::BINDING_SAML2_HTTP_POST; + } elseif (array_key_exists('SAMLart', $post)) { + return SamlConstants::BINDING_SAML2_HTTP_ARTIFACT; + } else { + if ($contentType = $request->headers->get('CONTENT_TYPE')) { + // Remove charset + if (false !== $pos = strpos($contentType, ';')) { + $contentType = substr($contentType, 0, $pos); + } + + if ('text/xml' === $contentType) { + return SamlConstants::BINDING_SAML2_SOAP; + } + } + } + + return null; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Binding/BindingFactoryInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Binding/BindingFactoryInterface.php new file mode 100644 index 0000000000..daddab19b1 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Binding/BindingFactoryInterface.php @@ -0,0 +1,40 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Binding; + +use Symfony\Component\HttpFoundation\Request; + +interface BindingFactoryInterface +{ + /** + * @param Request $request + * + * @return AbstractBinding + */ + public function getBindingByRequest(Request $request); + + /** + * @param string $bindingType + * + * @throws \LightSaml\Error\LightSamlBindingException + * + * @return AbstractBinding + */ + public function create($bindingType); + + /** + * @param Request $request + * + * @return string|null + */ + public function detectBindingType(Request $request); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Binding/HttpPostBinding.php b/vendor/lightsaml/lightsaml/src/LightSaml/Binding/HttpPostBinding.php new file mode 100644 index 0000000000..03db6e4f8f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Binding/HttpPostBinding.php @@ -0,0 +1,83 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Binding; + +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\MessageContext; +use LightSaml\Error\LightSamlBindingException; +use LightSaml\Model\Protocol\AbstractRequest; +use LightSaml\Model\Protocol\SamlMessage; +use Symfony\Component\HttpFoundation\Request; + +class HttpPostBinding extends AbstractBinding +{ + /** + * @param MessageContext $context + * @param null|string $destination + * + * @return SamlPostResponse + */ + public function send(MessageContext $context, $destination = null) + { + $message = MessageContextHelper::asSamlMessage($context); + $destination = $message->getDestination() ? $message->getDestination() : $destination; + + $serializationContext = $context->getSerializationContext(); + $message->serialize($serializationContext->getDocument(), $serializationContext); + $msgStr = $serializationContext->getDocument()->saveXML(); + + $this->dispatchSend($msgStr); + + $msgStr = base64_encode($msgStr); + + $type = $message instanceof AbstractRequest ? 'SAMLRequest' : 'SAMLResponse'; + + $data = array($type => $msgStr); + if ($message->getRelayState()) { + $data['RelayState'] = $message->getRelayState(); + } + + $result = new SamlPostResponse($destination, $data); + $result->renderContent(); + + return $result; + } + + /** + * @param Request $request + * @param MessageContext $context + */ + public function receive(Request $request, MessageContext $context) + { + $post = $request->request->all(); + if (array_key_exists('SAMLRequest', $post)) { + $msg = $post['SAMLRequest']; + } elseif (array_key_exists('SAMLResponse', $post)) { + $msg = $post['SAMLResponse']; + } else { + throw new LightSamlBindingException('Missing SAMLRequest or SAMLResponse parameter'); + } + + $msg = base64_decode($msg); + + $this->dispatchReceive($msg); + + $deserializationContext = $context->getDeserializationContext(); + $result = SamlMessage::fromXML($msg, $deserializationContext); + + if (array_key_exists('RelayState', $post)) { + $result->setRelayState($post['RelayState']); + } + + $context->setMessage($result); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Binding/HttpRedirectBinding.php b/vendor/lightsaml/lightsaml/src/LightSaml/Binding/HttpRedirectBinding.php new file mode 100644 index 0000000000..a6bc888b31 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Binding/HttpRedirectBinding.php @@ -0,0 +1,310 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Binding; + +use LightSaml\Context\Profile\Helper\MessageContextHelper; +use LightSaml\Context\Profile\MessageContext; +use LightSaml\Error\LightSamlBindingException; +use LightSaml\Model\Protocol\AbstractRequest; +use LightSaml\Model\Protocol\SamlMessage; +use LightSaml\Model\XmlDSig\SignatureWriter; +use LightSaml\Model\XmlDSig\SignatureStringReader; +use LightSaml\SamlConstants; +use RobRichards\XMLSecLibs\XMLSecurityKey; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; + +class HttpRedirectBinding extends AbstractBinding +{ + /** + * @param MessageContext $context + * @param null|string $destination + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function send(MessageContext $context, $destination = null) + { + $destination = $context->getMessage()->getDestination() ? $context->getMessage()->getDestination() : $destination; + + $url = $this->getRedirectURL($context, $destination); + + return new RedirectResponse($url); + } + + /** + * @param Request $request + * @param MessageContext $context + */ + public function receive(Request $request, MessageContext $context) + { + $data = $this->parseQuery($request); + + $this->processData($data, $context); + } + + /** + * @param array $data + * @param MessageContext $context + * + * @throws \Exception + */ + protected function processData(array $data, MessageContext $context) + { + $msg = $this->getMessageStringFromData($data); + $encoding = $this->getEncodingFromData($data); + $msg = $this->decodeMessageString($msg, $encoding); + + $this->dispatchReceive($msg); + + $deserializationContext = $context->getDeserializationContext(); + $message = SamlMessage::fromXML($msg, $deserializationContext); + + $this->loadRelayState($message, $data); + $this->loadSignature($message, $data); + + $context->setMessage($message); + } + + /** + * @param array $data + * + * @return string + * + * @throws LightSamlBindingException + */ + protected function getMessageStringFromData(array $data) + { + if (array_key_exists('SAMLRequest', $data)) { + return $data['SAMLRequest']; + } elseif (array_key_exists('SAMLResponse', $data)) { + return $data['SAMLResponse']; + } else { + throw new LightSamlBindingException('Missing SAMLRequest or SAMLResponse parameter'); + } + } + + /** + * @param array $data + * + * @return string + */ + protected function getEncodingFromData(array $data) + { + if (array_key_exists('SAMLEncoding', $data)) { + return $data['SAMLEncoding']; + } else { + return SamlConstants::ENCODING_DEFLATE; + } + } + + /** + * @param string $msg + * @param string $encoding + * + * @throws \LightSaml\Error\LightSamlBindingException + * + * @return string + */ + protected function decodeMessageString($msg, $encoding) + { + $msg = base64_decode($msg); + switch ($encoding) { + case SamlConstants::ENCODING_DEFLATE: + return gzinflate($msg); + break; + default: + throw new LightSamlBindingException(sprintf("Unknown encoding '%s'", $encoding)); + } + } + + protected function loadRelayState(SamlMessage $message, array $data) + { + if (array_key_exists('RelayState', $data)) { + $message->setRelayState($data['RelayState']); + } + } + + protected function loadSignature(SamlMessage $message, array $data) + { + if (array_key_exists('Signature', $data)) { + if (false == array_key_exists('SigAlg', $data)) { + throw new LightSamlBindingException('Missing signature algorithm'); + } + $message->setSignature( + new SignatureStringReader($data['Signature'], $data['SigAlg'], $data['SignedQuery']) + ); + } + } + + /** + * @param MessageContext $context + * @param string|null $destination + * + * @return string + */ + protected function getRedirectURL(MessageContext $context, $destination) + { + $message = MessageContextHelper::asSamlMessage($context); + $signature = $message->getSignature(); + if ($signature && false == $signature instanceof SignatureWriter) { + throw new LightSamlBindingException('Signature must be SignatureWriter'); + } + + $xml = $this->getMessageEncodedXml($message, $context); + $msg = $this->addMessageToUrl($message, $xml); + $this->addRelayStateToUrl($msg, $message); + $this->addSignatureToUrl($msg, $signature); + + return $this->getDestinationUrl($msg, $message, $destination); + } + + /** + * @param SamlMessage $message + * @param MessageContext $context + * + * @return string + */ + protected function getMessageEncodedXml(SamlMessage $message, MessageContext $context) + { + $message->setSignature(null); + + $serializationContext = $context->getSerializationContext(); + $message->serialize($serializationContext->getDocument(), $serializationContext); + $xml = $serializationContext->getDocument()->saveXML(); + + $this->dispatchSend($xml); + + $xml = gzdeflate($xml); + $xml = base64_encode($xml); + + return $xml; + } + + /** + * @param SamlMessage $message + * @param string $xml + * + * @return string + */ + protected function addMessageToUrl(SamlMessage $message, $xml) + { + if ($message instanceof AbstractRequest) { + $msg = 'SAMLRequest='; + } else { + $msg = 'SAMLResponse='; + } + $msg .= urlencode($xml); + + return $msg; + } + + /** + * @param string $msg + * @param SamlMessage $message + */ + protected function addRelayStateToUrl(&$msg, SamlMessage $message) + { + if (null !== $message->getRelayState()) { + $msg .= '&RelayState='.urlencode($message->getRelayState()); + } + } + + /** + * @param string $msg + * @param SignatureWriter|null $signature + */ + protected function addSignatureToUrl(&$msg, SignatureWriter $signature = null) + { + /** @var $key XMLSecurityKey */ + $key = $signature ? $signature->getXmlSecurityKey() : null; + + if (null != $key) { + $msg .= '&SigAlg='.urlencode($key->type); + $signature = $key->signData($msg); + $msg .= '&Signature='.urlencode(base64_encode($signature)); + } + } + + /** + * @param string $msg + * @param SamlMessage $message + * @param string|null $destination + * + * @return string + */ + protected function getDestinationUrl($msg, SamlMessage $message, $destination) + { + $destination = $message->getDestination() ? $message->getDestination() : $destination; + if (false === strpos($destination, '?')) { + $destination .= '?'.$msg; + } else { + $destination .= '&'.$msg; + } + + return $destination; + } + + /** + * @param Request $request + * + * @return array + */ + protected function parseQuery(Request $request) + { + /* + * Parse the query string. We need to do this ourself, so that we get access + * to the raw (urlencoded) values. This is required because different software + * can urlencode to different values. + */ + $sigQuery = $relayState = $sigAlg = ''; + $data = $this->parseQueryString($request->server->get('QUERY_STRING'), false); + $result = array(); + foreach ($data as $name => $value) { + $result[$name] = urldecode($value); + switch ($name) { + case 'SAMLRequest': + case 'SAMLResponse': + $sigQuery = $name.'='.$value; + break; + case 'RelayState': + $relayState = '&RelayState='.$value; + break; + case 'SigAlg': + $sigAlg = '&SigAlg='.$value; + break; + } + } + $result['SignedQuery'] = $sigQuery.$relayState.$sigAlg; + + return $result; + } + + /** + * @param string $queryString + * @param bool $urlDecodeValues + * + * @return array + */ + protected function parseQueryString($queryString, $urlDecodeValues = false) + { + $result = array(); + foreach (explode('&', $queryString) as $e) { + $tmp = explode('=', $e, 2); + $name = $tmp[0]; + $value = 2 === count($tmp) ? $value = $tmp[1] : ''; + $name = urldecode($name); + $result[$name] = $urlDecodeValues ? urldecode($value) : $value; + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Binding/SamlPostResponse.php b/vendor/lightsaml/lightsaml/src/LightSaml/Binding/SamlPostResponse.php new file mode 100644 index 0000000000..357f1f221f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Binding/SamlPostResponse.php @@ -0,0 +1,96 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Binding; + +use Symfony\Component\HttpFoundation\Response; + +class SamlPostResponse extends Response +{ + /** @var string */ + protected $destination; + + /** @var array */ + protected $data; + + /** + * @param string $destination + * @param array $data + * @param int $status + * @param array $headers + */ + public function __construct($destination, array $data, $status = 200, $headers = array()) + { + parent::__construct('', $status, $headers); + + $this->destination = $destination; + $this->data = $data; + } + + /** + * @return array + */ + public function getData() + { + return $this->data; + } + + /** + * @return string + */ + public function getDestination() + { + return $this->destination; + } + + public function renderContent() + { + $content = <<<'EOT' + + + + + POST data + + + + + +
+ + + %s + + + +
+ + +EOT; + $fields = ''; + foreach ($this->data as $name => $value) { + $fields .= sprintf( + '', + htmlspecialchars($name), + htmlspecialchars($value) + ); + } + + $content = sprintf($content, htmlspecialchars($this->destination), $fields); + + $this->setContent($content); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/AbstractPimpleContainer.php b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/AbstractPimpleContainer.php new file mode 100644 index 0000000000..fceb1688b5 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/AbstractPimpleContainer.php @@ -0,0 +1,36 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Bridge\Pimple\Container; + +use Pimple\Container; + +abstract class AbstractPimpleContainer +{ + /** @var Container */ + protected $pimple; + + /** + * @param Container $pimple + */ + public function __construct(Container $pimple) + { + $this->pimple = $pimple; + } + + /** + * @return Container + */ + public function getPimple() + { + return $this->pimple; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/BuildContainer.php b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/BuildContainer.php new file mode 100644 index 0000000000..935410fae6 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/BuildContainer.php @@ -0,0 +1,129 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Bridge\Pimple\Container; + +use LightSaml\Build\Container\BuildContainerInterface; +use LightSaml\Build\Container\CredentialContainerInterface; +use LightSaml\Build\Container\OwnContainerInterface; +use LightSaml\Build\Container\PartyContainerInterface; +use LightSaml\Build\Container\ProviderContainerInterface; +use LightSaml\Build\Container\ServiceContainerInterface; +use LightSaml\Build\Container\StoreContainerInterface; +use LightSaml\Build\Container\SystemContainerInterface; + +class BuildContainer extends AbstractPimpleContainer implements BuildContainerInterface +{ + /** @var SystemContainerInterface */ + private $systemContainer; + + /** @var PartyContainerInterface */ + private $partyContainer; + + /** @var StoreContainerInterface */ + private $storeContainer; + + /** @var ProviderContainerInterface */ + private $providerContainer; + + /** @var CredentialContainerInterface */ + private $credentialContainer; + + /** @var ServiceContainerInterface */ + private $serviceContainer; + + /** @var OwnContainerInterface */ + private $ownContainer; + + /** + * @return SystemContainerInterface + */ + public function getSystemContainer() + { + if (null == $this->systemContainer) { + $this->systemContainer = new SystemContainer($this->pimple); + } + + return $this->systemContainer; + } + + /** + * @return PartyContainerInterface + */ + public function getPartyContainer() + { + if (null == $this->partyContainer) { + $this->partyContainer = new PartyContainer($this->pimple); + } + + return $this->partyContainer; + } + + /** + * @return StoreContainerInterface + */ + public function getStoreContainer() + { + if (null == $this->storeContainer) { + $this->storeContainer = new StoreContainer($this->pimple); + } + + return $this->storeContainer; + } + + /** + * @return ProviderContainerInterface + */ + public function getProviderContainer() + { + if (null == $this->providerContainer) { + $this->providerContainer = new ProviderContainer($this->pimple); + } + + return $this->providerContainer; + } + + /** + * @return CredentialContainerInterface + */ + public function getCredentialContainer() + { + if (null == $this->credentialContainer) { + $this->credentialContainer = new CredentialContainer($this->pimple); + } + + return $this->credentialContainer; + } + + /** + * @return ServiceContainerInterface + */ + public function getServiceContainer() + { + if (null == $this->serviceContainer) { + $this->serviceContainer = new ServiceContainer($this->pimple); + } + + return $this->serviceContainer; + } + + /** + * @return OwnContainerInterface + */ + public function getOwnContainer() + { + if (null == $this->ownContainer) { + $this->ownContainer = new OwnContainer($this->pimple); + } + + return $this->ownContainer; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/CredentialContainer.php b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/CredentialContainer.php new file mode 100644 index 0000000000..641b79c23d --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/CredentialContainer.php @@ -0,0 +1,28 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Bridge\Pimple\Container; + +use LightSaml\Build\Container\CredentialContainerInterface; +use LightSaml\Store\Credential\CredentialStoreInterface; + +class CredentialContainer extends AbstractPimpleContainer implements CredentialContainerInterface +{ + const CREDENTIAL_STORE = 'lightsaml.container.credential_store'; + + /** + * @return CredentialStoreInterface + */ + public function getCredentialStore() + { + return $this->pimple[self::CREDENTIAL_STORE]; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/CredentialContainerProvider.php b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/CredentialContainerProvider.php new file mode 100644 index 0000000000..3c682eac7a --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/CredentialContainerProvider.php @@ -0,0 +1,81 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Bridge\Pimple\Container\Factory; + +use LightSaml\Bridge\Pimple\Container\CredentialContainer; +use LightSaml\Build\Container\OwnContainerInterface; +use LightSaml\Build\Container\PartyContainerInterface; +use LightSaml\Credential\CredentialInterface; +use LightSaml\Error\LightSamlBuildException; +use LightSaml\Store\Credential\Factory\CredentialFactory; +use Pimple\Container; +use Pimple\ServiceProviderInterface; + +class CredentialContainerProvider implements ServiceProviderInterface +{ + /** @var PartyContainerInterface */ + private $partyContainer; + + /** @var OwnContainerInterface */ + private $ownContainer; + + /** @var CredentialInterface[] */ + private $extraCredentials = array(); + + /** + * @param PartyContainerInterface $partyContainer + * @param OwnContainerInterface $ownContainer + */ + public function __construct(PartyContainerInterface $partyContainer, OwnContainerInterface $ownContainer) + { + $this->ownContainer = $ownContainer; + $this->partyContainer = $partyContainer; + } + + /** + * @param CredentialInterface $credential + * + * @return CredentialContainerProvider + */ + public function addExtraCredential(CredentialInterface $credential) + { + if (null === $credential->getEntityId()) { + throw new \InvalidArgumentException('Extra credential must have entityID'); + } + + $this->extraCredentials[] = $credential; + + return $this; + } + + /** + * @param Container $pimple A container instance + */ + public function register(Container $pimple) + { + $ownCredentials = $this->ownContainer->getOwnCredentials(); + if (empty($ownCredentials)) { + throw new LightSamlBuildException('There are no own credentials'); + } + + $pimple[CredentialContainer::CREDENTIAL_STORE] = function () { + $factory = new CredentialFactory(); + + return $factory->build( + $this->partyContainer->getIdpEntityDescriptorStore(), + $this->partyContainer->getSpEntityDescriptorStore(), + $this->ownContainer->getOwnCredentials(), + $this->extraCredentials + ); + }; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/OwnContainerProvider.php b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/OwnContainerProvider.php new file mode 100644 index 0000000000..dd8fa95f63 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/OwnContainerProvider.php @@ -0,0 +1,72 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Bridge\Pimple\Container\Factory; + +use LightSaml\Bridge\Pimple\Container\OwnContainer; +use LightSaml\Credential\CredentialInterface; +use LightSaml\Error\LightSamlBuildException; +use LightSaml\Provider\EntityDescriptor\EntityDescriptorProviderInterface; +use Pimple\Container; +use Pimple\ServiceProviderInterface; + +class OwnContainerProvider implements ServiceProviderInterface +{ + /** @var CredentialInterface[] */ + private $ownCredentials = array(); + + /** @var EntityDescriptorProviderInterface */ + private $ownEntityDescriptorProvider; + + /** + * @param EntityDescriptorProviderInterface $ownEntityDescriptorProvider + * @param CredentialInterface[] $ownCredentials + */ + public function __construct(EntityDescriptorProviderInterface $ownEntityDescriptorProvider, array $ownCredentials = null) + { + $this->ownEntityDescriptorProvider = $ownEntityDescriptorProvider; + if ($ownCredentials) { + foreach ($ownCredentials as $credential) { + $this->addOwnCredential($credential); + } + } + } + + /** + * @param CredentialInterface $credential + * + * @return OwnContainerProvider + */ + public function addOwnCredential(CredentialInterface $credential) + { + if (null == $credential->getPrivateKey()) { + throw new LightSamlBuildException('Own credential must have private key'); + } + + $this->ownCredentials[] = $credential; + + return $this; + } + + /** + * @param Container $pimple A container instance + */ + public function register(Container $pimple) + { + $pimple[OwnContainer::OWN_CREDENTIALS] = function () { + return $this->ownCredentials; + }; + + $pimple[OwnContainer::OWN_ENTITY_DESCRIPTOR_PROVIDER] = function () { + return $this->ownEntityDescriptorProvider; + }; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/PartyContainerProvider.php b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/PartyContainerProvider.php new file mode 100644 index 0000000000..361c361631 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/PartyContainerProvider.php @@ -0,0 +1,40 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Bridge\Pimple\Container\Factory; + +use LightSaml\Bridge\Pimple\Container\PartyContainer; +use LightSaml\Meta\TrustOptions\TrustOptions; +use LightSaml\Store\EntityDescriptor\FixedEntityDescriptorStore; +use LightSaml\Store\TrustOptions\FixedTrustOptionsStore; +use Pimple\Container; +use Pimple\ServiceProviderInterface; + +class PartyContainerProvider implements ServiceProviderInterface +{ + /** + * @param Container $pimple A container instance + */ + public function register(Container $pimple) + { + $pimple[PartyContainer::IDP_ENTITY_DESCRIPTOR] = function () { + return new FixedEntityDescriptorStore(); + }; + + $pimple[PartyContainer::SP_ENTITY_DESCRIPTOR] = function () { + return new FixedEntityDescriptorStore(); + }; + + $pimple[PartyContainer::TRUST_OPTIONS_STORE] = function () { + return new FixedTrustOptionsStore(new TrustOptions()); + }; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/ProviderContainerProvider.php b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/ProviderContainerProvider.php new file mode 100644 index 0000000000..26b1c9fe67 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/ProviderContainerProvider.php @@ -0,0 +1,38 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Bridge\Pimple\Container\Factory; + +use LightSaml\Bridge\Pimple\Container\ProviderContainer; +use LightSaml\Error\LightSamlBuildException; +use Pimple\Container; +use Pimple\ServiceProviderInterface; + +class ProviderContainerProvider implements ServiceProviderInterface +{ + /** + * @param Container $pimple A container instance + */ + public function register(Container $pimple) + { + $pimple[ProviderContainer::ATTRIBUTE_VALUE_PROVIDER] = function () { + throw new LightSamlBuildException('Attribute value provider not set'); + }; + + $pimple[ProviderContainer::SESSION_INFO_PROVIDER] = function () { + throw new LightSamlBuildException('Session info provider not set'); + }; + + $pimple[ProviderContainer::NAME_ID_PROVIDER] = function () { + throw new LightSamlBuildException('Name ID provider not set'); + }; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/ServiceContainerProvider.php b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/ServiceContainerProvider.php new file mode 100644 index 0000000000..15581697a9 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/ServiceContainerProvider.php @@ -0,0 +1,127 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Bridge\Pimple\Container\Factory; + +use LightSaml\Binding\BindingFactory; +use LightSaml\Bridge\Pimple\Container\ServiceContainer; +use LightSaml\Build\Container\CredentialContainerInterface; +use LightSaml\Build\Container\StoreContainerInterface; +use LightSaml\Build\Container\SystemContainerInterface; +use LightSaml\Resolver\Credential\Factory\CredentialResolverFactory; +use LightSaml\Resolver\Endpoint\BindingEndpointResolver; +use LightSaml\Resolver\Endpoint\CompositeEndpointResolver; +use LightSaml\Resolver\Endpoint\DescriptorTypeEndpointResolver; +use LightSaml\Resolver\Endpoint\IndexEndpointResolver; +use LightSaml\Resolver\Endpoint\LocationEndpointResolver; +use LightSaml\Resolver\Endpoint\ServiceTypeEndpointResolver; +use LightSaml\Resolver\Logout\LogoutSessionResolver; +use LightSaml\Resolver\Session\SessionProcessor; +use LightSaml\Resolver\Signature\OwnSignatureResolver; +use LightSaml\Validator\Model\Assertion\AssertionTimeValidator; +use LightSaml\Validator\Model\Assertion\AssertionValidator; +use LightSaml\Validator\Model\NameId\NameIdValidator; +use LightSaml\Validator\Model\Signature\SignatureValidator; +use LightSaml\Validator\Model\Statement\StatementValidator; +use LightSaml\Validator\Model\Subject\SubjectValidator; +use Pimple\Container; +use Pimple\ServiceProviderInterface; + +class ServiceContainerProvider implements ServiceProviderInterface +{ + /** @var CredentialContainerInterface */ + private $credentialContainer; + + /** @var SystemContainerInterface */ + private $systemContainer; + + /** @var StoreContainerInterface */ + private $storeContainer; + + /** + * @param CredentialContainerInterface $credentialContainer + * @param StoreContainerInterface $storeContainer + * @param SystemContainerInterface $systemContainer + */ + public function __construct( + CredentialContainerInterface $credentialContainer, + StoreContainerInterface $storeContainer, + SystemContainerInterface $systemContainer + ) { + $this->credentialContainer = $credentialContainer; + $this->storeContainer = $storeContainer; + $this->systemContainer = $systemContainer; + } + + /** + * @param Container $pimple A container instance + */ + public function register(Container $pimple) + { + $pimple[ServiceContainer::NAME_ID_VALIDATOR] = function () { + return new NameIdValidator(); + }; + + $pimple[ServiceContainer::ASSERTION_TIME_VALIDATOR] = function () { + return new AssertionTimeValidator(); + }; + + $pimple[ServiceContainer::ASSERTION_VALIDATOR] = function (Container $c) { + $nameIdValidator = $c[ServiceContainer::NAME_ID_VALIDATOR]; + + return new AssertionValidator( + $nameIdValidator, + new SubjectValidator($nameIdValidator), + new StatementValidator() + ); + }; + + $pimple[ServiceContainer::ENDPOINT_RESOLVER] = function () { + return new CompositeEndpointResolver(array( + new BindingEndpointResolver(), + new DescriptorTypeEndpointResolver(), + new ServiceTypeEndpointResolver(), + new IndexEndpointResolver(), + new LocationEndpointResolver(), + )); + }; + + $pimple[ServiceContainer::BINDING_FACTORY] = function () { + return new BindingFactory($this->systemContainer->getEventDispatcher()); + }; + + $pimple[ServiceContainer::CREDENTIAL_RESOLVER] = function () { + $factory = new CredentialResolverFactory($this->credentialContainer->getCredentialStore()); + + return $factory->build(); + }; + + $pimple[ServiceContainer::SIGNATURE_RESOLVER] = function (Container $c) { + $credentialResolver = $c[ServiceContainer::CREDENTIAL_RESOLVER]; + + return new OwnSignatureResolver($credentialResolver); + }; + + $pimple[ServiceContainer::SIGNATURE_VALIDATOR] = function (Container $c) { + $credentialResolver = $c[ServiceContainer::CREDENTIAL_RESOLVER]; + + return new SignatureValidator($credentialResolver); + }; + + $pimple[ServiceContainer::LOGOUT_SESSION_RESOLVER] = function () { + return new LogoutSessionResolver($this->storeContainer->getSsoStateStore()); + }; + + $pimple[ServiceContainer::SESSION_PROCESSOR] = function () { + return new SessionProcessor($this->storeContainer->getSsoStateStore(), $this->systemContainer->getTimeProvider()); + }; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/StoreContainerProvider.php b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/StoreContainerProvider.php new file mode 100644 index 0000000000..c5de07c0e4 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/StoreContainerProvider.php @@ -0,0 +1,49 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Bridge\Pimple\Container\Factory; + +use LightSaml\Bridge\Pimple\Container\StoreContainer; +use LightSaml\Build\Container\SystemContainerInterface; +use LightSaml\Store\Id\NullIdStore; +use LightSaml\Store\Request\RequestStateSessionStore; +use LightSaml\Store\Sso\SsoStateSessionStore; +use Pimple\Container; +use Pimple\ServiceProviderInterface; + +class StoreContainerProvider implements ServiceProviderInterface +{ + /** @var SystemContainerInterface */ + private $systemContainer; + + public function __construct(SystemContainerInterface $systemContainer) + { + $this->systemContainer = $systemContainer; + } + + /** + * @param Container $pimple A container instance + */ + public function register(Container $pimple) + { + $pimple[StoreContainer::REQUEST_STATE_STORE] = function () { + return new RequestStateSessionStore($this->systemContainer->getSession(), 'main'); + }; + + $pimple[StoreContainer::ID_STATE_STORE] = function () { + return new NullIdStore(); + }; + + $pimple[StoreContainer::SSO_STATE_STORE] = function () { + return new SsoStateSessionStore($this->systemContainer->getSession(), 'samlsso'); + }; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/SystemContainerProvider.php b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/SystemContainerProvider.php new file mode 100644 index 0000000000..4638c89c37 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/Factory/SystemContainerProvider.php @@ -0,0 +1,67 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Bridge\Pimple\Container\Factory; + +use LightSaml\Bridge\Pimple\Container\SystemContainer; +use LightSaml\Provider\TimeProvider\SystemTimeProvider; +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Psr\Log\NullLogger; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; + +class SystemContainerProvider implements ServiceProviderInterface +{ + /** @var bool */ + private $mockSession; + + public function __construct($mockSession = false) + { + $this->mockSession = true; + } + + /** + * @param Container $pimple A container instance + */ + public function register(Container $pimple) + { + $pimple[SystemContainer::REQUEST] = function () { + return Request::createFromGlobals(); + }; + + $pimple[SystemContainer::SESSION] = function () { + if ($this->mockSession) { + $session = new Session(new MockArraySessionStorage()); + } else { + $session = new Session(); + } + $session->setName(sprintf('SID%s', mt_rand(1000, 9999))); + $session->start(); + + return $session; + }; + + $pimple[SystemContainer::TIME_PROVIDER] = function () { + return new SystemTimeProvider(); + }; + + $pimple[SystemContainer::EVENT_DISPATCHER] = function () { + return new EventDispatcher(); + }; + + $pimple[SystemContainer::LOGGER] = function () { + return new NullLogger(); + }; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/OwnContainer.php b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/OwnContainer.php new file mode 100644 index 0000000000..d554ee9435 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/OwnContainer.php @@ -0,0 +1,38 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Bridge\Pimple\Container; + +use LightSaml\Build\Container\OwnContainerInterface; +use LightSaml\Provider\EntityDescriptor\EntityDescriptorProviderInterface; +use LightSaml\Credential\CredentialInterface; + +class OwnContainer extends AbstractPimpleContainer implements OwnContainerInterface +{ + const OWN_ENTITY_DESCRIPTOR_PROVIDER = 'lightsaml.container.own_entity_descriptor_provider'; + const OWN_CREDENTIALS = 'lightsaml.container.own_credentials'; + + /** + * @return EntityDescriptorProviderInterface + */ + public function getOwnEntityDescriptorProvider() + { + return $this->pimple[self::OWN_ENTITY_DESCRIPTOR_PROVIDER]; + } + + /** + * @return CredentialInterface[] + */ + public function getOwnCredentials() + { + return $this->pimple[self::OWN_CREDENTIALS]; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/PartyContainer.php b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/PartyContainer.php new file mode 100644 index 0000000000..b065697e64 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/PartyContainer.php @@ -0,0 +1,47 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Bridge\Pimple\Container; + +use LightSaml\Build\Container\PartyContainerInterface; +use LightSaml\Store\EntityDescriptor\EntityDescriptorStoreInterface; +use LightSaml\Store\TrustOptions\TrustOptionsStoreInterface; + +class PartyContainer extends AbstractPimpleContainer implements PartyContainerInterface +{ + const IDP_ENTITY_DESCRIPTOR = 'lightsaml.container.idp_entity_descriptor'; + const SP_ENTITY_DESCRIPTOR = 'lightsaml.container.sp_entity_descriptor'; + const TRUST_OPTIONS_STORE = 'lightsaml.container.trust_options_store'; + + /** + * @return EntityDescriptorStoreInterface + */ + public function getIdpEntityDescriptorStore() + { + return $this->pimple[self::IDP_ENTITY_DESCRIPTOR]; + } + + /** + * @return EntityDescriptorStoreInterface + */ + public function getSpEntityDescriptorStore() + { + return $this->pimple[self::SP_ENTITY_DESCRIPTOR]; + } + + /** + * @return TrustOptionsStoreInterface + */ + public function getTrustOptionsStore() + { + return $this->pimple[self::TRUST_OPTIONS_STORE]; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/ProviderContainer.php b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/ProviderContainer.php new file mode 100644 index 0000000000..42cfbf6854 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/ProviderContainer.php @@ -0,0 +1,61 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Bridge\Pimple\Container; + +use LightSaml\Build\Container\ProviderContainerInterface; +use LightSaml\Error\LightSamlBuildException; +use LightSaml\Provider\Attribute\AttributeValueProviderInterface; +use LightSaml\Provider\NameID\NameIdProviderInterface; +use LightSaml\Provider\Session\SessionInfoProviderInterface; + +class ProviderContainer extends AbstractPimpleContainer implements ProviderContainerInterface +{ + const ATTRIBUTE_VALUE_PROVIDER = 'lightsaml.container.attribute_value_provider'; + const SESSION_INFO_PROVIDER = 'lightsaml.container.session_info_provider'; + const NAME_ID_PROVIDER = 'lightsaml.container.name_id_provider'; + + /** + * @return AttributeValueProviderInterface + */ + public function getAttributeValueProvider() + { + if (isset($this->pimple[self::ATTRIBUTE_VALUE_PROVIDER])) { + return $this->pimple[self::ATTRIBUTE_VALUE_PROVIDER]; + } + + throw new LightSamlBuildException('Attribute value provider not set'); + } + + /** + * @return SessionInfoProviderInterface + */ + public function getSessionInfoProvider() + { + if (isset($this->pimple[self::SESSION_INFO_PROVIDER])) { + return $this->pimple[self::SESSION_INFO_PROVIDER]; + } + + throw new LightSamlBuildException('Session info provider not set'); + } + + /** + * @return NameIdProviderInterface + */ + public function getNameIdProvider() + { + if (isset($this->pimple[self::NAME_ID_PROVIDER])) { + return $this->pimple[self::NAME_ID_PROVIDER]; + } + + throw new LightSamlBuildException('Name ID provider not set'); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/ServiceContainer.php b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/ServiceContainer.php new file mode 100644 index 0000000000..15b6c641fc --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/ServiceContainer.php @@ -0,0 +1,118 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Bridge\Pimple\Container; + +use LightSaml\Binding\BindingFactoryInterface; +use LightSaml\Build\Container\ServiceContainerInterface; +use LightSaml\Resolver\Credential\CredentialResolverInterface; +use LightSaml\Resolver\Endpoint\EndpointResolverInterface; +use LightSaml\Logout\Resolver\Logout\LogoutSessionResolverInterface; +use LightSaml\Resolver\Session\SessionProcessorInterface; +use LightSaml\Resolver\Signature\SignatureResolverInterface; +use LightSaml\Validator\Model\Assertion\AssertionTimeValidator; +use LightSaml\Validator\Model\Assertion\AssertionValidatorInterface; +use LightSaml\Validator\Model\NameId\NameIdValidatorInterface; +use LightSaml\Validator\Model\Signature\SignatureValidatorInterface; + +class ServiceContainer extends AbstractPimpleContainer implements ServiceContainerInterface +{ + const ASSERTION_VALIDATOR = 'lightsaml.container.assertion_validator'; + const ASSERTION_TIME_VALIDATOR = 'lightsaml.container.assertion_time_validator'; + const SIGNATURE_RESOLVER = 'lightsaml.container.signature_resolver'; + const ENDPOINT_RESOLVER = 'lightsaml.container.endpoint_resolver'; + const NAME_ID_VALIDATOR = 'lightsaml.container.name_id_validator'; + const BINDING_FACTORY = 'lightsaml.container.binding_factory'; + const SIGNATURE_VALIDATOR = 'lightsaml.container.signature_validator'; + const CREDENTIAL_RESOLVER = 'lightsaml.container.credential_resolver'; + const LOGOUT_SESSION_RESOLVER = 'lightsaml.container.logout_session_resolver'; + const SESSION_PROCESSOR = 'lightsaml.container.session_processor'; + + /** + * @return AssertionValidatorInterface + */ + public function getAssertionValidator() + { + return $this->pimple[self::ASSERTION_VALIDATOR]; + } + + /** + * @return AssertionTimeValidator + */ + public function getAssertionTimeValidator() + { + return $this->pimple[self::ASSERTION_TIME_VALIDATOR]; + } + + /** + * @return SignatureResolverInterface + */ + public function getSignatureResolver() + { + return $this->pimple[self::SIGNATURE_RESOLVER]; + } + + /** + * @return EndpointResolverInterface + */ + public function getEndpointResolver() + { + return $this->pimple[self::ENDPOINT_RESOLVER]; + } + + /** + * @return NameIdValidatorInterface + */ + public function getNameIdValidator() + { + return $this->pimple[self::NAME_ID_VALIDATOR]; + } + + /** + * @return BindingFactoryInterface + */ + public function getBindingFactory() + { + return $this->pimple[self::BINDING_FACTORY]; + } + + /** + * @return SignatureValidatorInterface + */ + public function getSignatureValidator() + { + return $this->pimple[self::SIGNATURE_VALIDATOR]; + } + + /** + * @return CredentialResolverInterface + */ + public function getCredentialResolver() + { + return $this->pimple[self::CREDENTIAL_RESOLVER]; + } + + /** + * @return LogoutSessionResolverInterface + */ + public function getLogoutSessionResolver() + { + return $this->pimple[self::LOGOUT_SESSION_RESOLVER]; + } + + /** + * @return SessionProcessorInterface + */ + public function getSessionProcessor() + { + return $this->pimple[self::SESSION_PROCESSOR]; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/StoreContainer.php b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/StoreContainer.php new file mode 100644 index 0000000000..d371279898 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/StoreContainer.php @@ -0,0 +1,48 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Bridge\Pimple\Container; + +use LightSaml\Build\Container\StoreContainerInterface; +use LightSaml\Store\Id\IdStoreInterface; +use LightSaml\Store\Request\RequestStateStoreInterface; +use LightSaml\Store\Sso\SsoStateStoreInterface; + +class StoreContainer extends AbstractPimpleContainer implements StoreContainerInterface +{ + const REQUEST_STATE_STORE = 'lightsaml.container.request_state_store'; + const ID_STATE_STORE = 'lightsaml.container.id_state_store'; + const SSO_STATE_STORE = 'lightsaml.container.sso_state_store'; + + /** + * @return RequestStateStoreInterface + */ + public function getRequestStateStore() + { + return $this->pimple[self::REQUEST_STATE_STORE]; + } + + /** + * @return IdStoreInterface + */ + public function getIdStateStore() + { + return $this->pimple[self::ID_STATE_STORE]; + } + + /** + * @return SsoStateStoreInterface + */ + public function getSsoStateStore() + { + return $this->pimple[self::SSO_STATE_STORE]; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/SystemContainer.php b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/SystemContainer.php new file mode 100644 index 0000000000..42f315ef1f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Bridge/Pimple/Container/SystemContainer.php @@ -0,0 +1,68 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Bridge\Pimple\Container; + +use LightSaml\Build\Container\SystemContainerInterface; +use LightSaml\Provider\TimeProvider\TimeProviderInterface; +use Psr\Log\LoggerInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Session\SessionInterface; + +class SystemContainer extends AbstractPimpleContainer implements SystemContainerInterface +{ + const REQUEST = 'lightsaml.container.request'; + const SESSION = 'lightsaml.container.session'; + const TIME_PROVIDER = 'lightsaml.container.time_provider'; + const EVENT_DISPATCHER = 'lightsaml.container.event_dispatcher'; + const LOGGER = 'lightsaml.container.logger'; + + /** + * @return Request + */ + public function getRequest() + { + return $this->pimple[self::REQUEST]; + } + + /** + * @return SessionInterface + */ + public function getSession() + { + return $this->pimple[self::SESSION]; + } + + /** + * @return TimeProviderInterface + */ + public function getTimeProvider() + { + return $this->pimple[self::TIME_PROVIDER]; + } + + /** + * @return EventDispatcherInterface + */ + public function getEventDispatcher() + { + return $this->pimple[self::EVENT_DISPATCHER]; + } + + /** + * @return LoggerInterface + */ + public function getLogger() + { + return $this->pimple[self::LOGGER]; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/BuildContainerInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/BuildContainerInterface.php new file mode 100644 index 0000000000..f6e218ddd5 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/BuildContainerInterface.php @@ -0,0 +1,50 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Build\Container; + +interface BuildContainerInterface +{ + /** + * @return SystemContainerInterface + */ + public function getSystemContainer(); + + /** + * @return PartyContainerInterface + */ + public function getPartyContainer(); + + /** + * @return StoreContainerInterface + */ + public function getStoreContainer(); + + /** + * @return ProviderContainerInterface + */ + public function getProviderContainer(); + + /** + * @return CredentialContainerInterface + */ + public function getCredentialContainer(); + + /** + * @return ServiceContainerInterface + */ + public function getServiceContainer(); + + /** + * @return OwnContainerInterface + */ + public function getOwnContainer(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/CredentialContainerInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/CredentialContainerInterface.php new file mode 100644 index 0000000000..722ea4b612 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/CredentialContainerInterface.php @@ -0,0 +1,22 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Build\Container; + +use LightSaml\Store\Credential\CredentialStoreInterface; + +interface CredentialContainerInterface +{ + /** + * @return CredentialStoreInterface + */ + public function getCredentialStore(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/OwnContainerInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/OwnContainerInterface.php new file mode 100644 index 0000000000..021eaa6601 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/OwnContainerInterface.php @@ -0,0 +1,28 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Build\Container; + +use LightSaml\Provider\EntityDescriptor\EntityDescriptorProviderInterface; +use LightSaml\Credential\CredentialInterface; + +interface OwnContainerInterface +{ + /** + * @return EntityDescriptorProviderInterface + */ + public function getOwnEntityDescriptorProvider(); + + /** + * @return CredentialInterface[] + */ + public function getOwnCredentials(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/PartyContainerInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/PartyContainerInterface.php new file mode 100644 index 0000000000..6fdb04f064 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/PartyContainerInterface.php @@ -0,0 +1,33 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Build\Container; + +use LightSaml\Store\EntityDescriptor\EntityDescriptorStoreInterface; +use LightSaml\Store\TrustOptions\TrustOptionsStoreInterface; + +interface PartyContainerInterface +{ + /** + * @return EntityDescriptorStoreInterface + */ + public function getIdpEntityDescriptorStore(); + + /** + * @return EntityDescriptorStoreInterface + */ + public function getSpEntityDescriptorStore(); + + /** + * @return TrustOptionsStoreInterface + */ + public function getTrustOptionsStore(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/ProviderContainerInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/ProviderContainerInterface.php new file mode 100644 index 0000000000..9417e0031c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/ProviderContainerInterface.php @@ -0,0 +1,34 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Build\Container; + +use LightSaml\Provider\Attribute\AttributeValueProviderInterface; +use LightSaml\Provider\NameID\NameIdProviderInterface; +use LightSaml\Provider\Session\SessionInfoProviderInterface; + +interface ProviderContainerInterface +{ + /** + * @return AttributeValueProviderInterface + */ + public function getAttributeValueProvider(); + + /** + * @return SessionInfoProviderInterface + */ + public function getSessionInfoProvider(); + + /** + * @return NameIdProviderInterface + */ + public function getNameIdProvider(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/ServiceContainerInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/ServiceContainerInterface.php new file mode 100644 index 0000000000..6f220fad74 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/ServiceContainerInterface.php @@ -0,0 +1,76 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Build\Container; + +use LightSaml\Binding\BindingFactoryInterface; +use LightSaml\Resolver\Credential\CredentialResolverInterface; +use LightSaml\Resolver\Endpoint\EndpointResolverInterface; +use LightSaml\Logout\Resolver\Logout\LogoutSessionResolverInterface; +use LightSaml\Resolver\Session\SessionProcessorInterface; +use LightSaml\Resolver\Signature\SignatureResolverInterface; +use LightSaml\Validator\Model\Assertion\AssertionTimeValidator; +use LightSaml\Validator\Model\Assertion\AssertionValidatorInterface; +use LightSaml\Validator\Model\NameId\NameIdValidatorInterface; +use LightSaml\Validator\Model\Signature\SignatureValidatorInterface; + +interface ServiceContainerInterface +{ + /** + * @return AssertionValidatorInterface + */ + public function getAssertionValidator(); + + /** + * @return AssertionTimeValidator + */ + public function getAssertionTimeValidator(); + + /** + * @return SignatureResolverInterface + */ + public function getSignatureResolver(); + + /** + * @return EndpointResolverInterface + */ + public function getEndpointResolver(); + + /** + * @return NameIdValidatorInterface + */ + public function getNameIdValidator(); + + /** + * @return BindingFactoryInterface + */ + public function getBindingFactory(); + + /** + * @return SignatureValidatorInterface + */ + public function getSignatureValidator(); + + /** + * @return CredentialResolverInterface + */ + public function getCredentialResolver(); + + /** + * @return LogoutSessionResolverInterface + */ + public function getLogoutSessionResolver(); + + /** + * @return SessionProcessorInterface + */ + public function getSessionProcessor(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/StoreContainerInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/StoreContainerInterface.php new file mode 100644 index 0000000000..390436e217 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/StoreContainerInterface.php @@ -0,0 +1,34 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Build\Container; + +use LightSaml\Store\Id\IdStoreInterface; +use LightSaml\Store\Request\RequestStateStoreInterface; +use LightSaml\Store\Sso\SsoStateStoreInterface; + +interface StoreContainerInterface +{ + /** + * @return RequestStateStoreInterface + */ + public function getRequestStateStore(); + + /** + * @return IdStoreInterface + */ + public function getIdStateStore(); + + /** + * @return SsoStateStoreInterface + */ + public function getSsoStateStore(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/SystemContainerInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/SystemContainerInterface.php new file mode 100644 index 0000000000..d99cd67717 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Build/Container/SystemContainerInterface.php @@ -0,0 +1,46 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Build\Container; + +use LightSaml\Provider\TimeProvider\TimeProviderInterface; +use Psr\Log\LoggerInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Session\SessionInterface; + +interface SystemContainerInterface +{ + /** + * @return Request + */ + public function getRequest(); + + /** + * @return SessionInterface + */ + public function getSession(); + + /** + * @return TimeProviderInterface + */ + public function getTimeProvider(); + + /** + * @return EventDispatcherInterface + */ + public function getEventDispatcher(); + + /** + * @return LoggerInterface + */ + public function getLogger(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/ActionBuilderInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/ActionBuilderInterface.php new file mode 100644 index 0000000000..ee6aa24bf5 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/ActionBuilderInterface.php @@ -0,0 +1,22 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Builder\Action; + +use LightSaml\Action\ActionInterface; + +interface ActionBuilderInterface +{ + /** + * @return ActionInterface + */ + public function build(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/CompositeActionBuilder.php b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/CompositeActionBuilder.php new file mode 100644 index 0000000000..ab8910fdc8 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/CompositeActionBuilder.php @@ -0,0 +1,74 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Builder\Action; + +use LightSaml\Action\ActionInterface; +use LightSaml\Action\CompositeAction; + +class CompositeActionBuilder implements ActionBuilderInterface +{ + /** + * int priority => ActionInterface[]. + * + * @var array + */ + private $actions = array(); + + /** @var int */ + protected $increaseStep = 5; + + /** @var int */ + private $biggestPriority = 0; + + /** + * @param ActionInterface $action + * @param int|bool $priority + * + * @return CompositeActionBuilder + */ + public function add(ActionInterface $action, $priority = false) + { + if (false === $priority) { + ++$this->biggestPriority; + $priority = $this->biggestPriority; + } elseif (false === is_int($priority)) { + throw new \InvalidArgumentException('Expected integer value for priority'); + } elseif ($priority > $this->biggestPriority) { + $this->biggestPriority = $priority; + } + + if (false === isset($this->actions[$priority])) { + $this->actions[$priority] = array(); + } + $this->actions[$priority][] = $action; + + return $this; + } + + /** + * @return CompositeAction + */ + public function build() + { + $actions = $this->actions; + ksort($actions); + + $result = new CompositeAction(); + foreach ($actions as $arr) { + foreach ($arr as $action) { + $result->add($action); + } + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/Profile/AbstractProfileActionBuilder.php b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/Profile/AbstractProfileActionBuilder.php new file mode 100644 index 0000000000..6171d18130 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/Profile/AbstractProfileActionBuilder.php @@ -0,0 +1,64 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Builder\Action\Profile; + +use LightSaml\Build\Container\BuildContainerInterface; +use LightSaml\Builder\Action\CompositeActionBuilder; +use LightSaml\Error\LightSamlBuildException; + +abstract class AbstractProfileActionBuilder extends CompositeActionBuilder +{ + /** @var BuildContainerInterface */ + protected $buildContainer; + + /** @var bool */ + private $initialized = false; + + /** + * @param BuildContainerInterface $buildContainer + */ + public function __construct(BuildContainerInterface $buildContainer) + { + $this->buildContainer = $buildContainer; + } + + /** + * @return void + */ + public function init() + { + if ($this->initialized) { + throw new LightSamlBuildException('Already initialized'); + } + + $this->doInitialize(); + + $this->initialized = true; + } + + /** + * @return void + */ + abstract protected function doInitialize(); + + /** + * @return \LightSaml\Action\ActionInterface + */ + public function build() + { + if (false === $this->initialized) { + $this->init(); + } + + return parent::build(); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/Profile/Metadata/MetadataActionBuilder.php b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/Profile/Metadata/MetadataActionBuilder.php new file mode 100644 index 0000000000..dd504bb353 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/Profile/Metadata/MetadataActionBuilder.php @@ -0,0 +1,26 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Builder\Action\Profile\Metadata; + +use LightSaml\Action\Profile\Entity\SerializeOwnEntityAction; +use LightSaml\Builder\Action\Profile\AbstractProfileActionBuilder; + +class MetadataActionBuilder extends AbstractProfileActionBuilder +{ + /** + * @return void + */ + protected function doInitialize() + { + $this->add(new SerializeOwnEntityAction($this->buildContainer->getSystemContainer()->getLogger()), 100); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/Profile/SingleSignOn/Sp/SsoSpReceiveResponseActionBuilder.php b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/Profile/SingleSignOn/Sp/SsoSpReceiveResponseActionBuilder.php new file mode 100644 index 0000000000..3675d50269 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/Profile/SingleSignOn/Sp/SsoSpReceiveResponseActionBuilder.php @@ -0,0 +1,127 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Builder\Action\Profile\SingleSignOn\Sp; + +use LightSaml\Action\Profile\FlushRequestStatesAction; +use LightSaml\Action\Profile\Inbound\Message\AssertBindingTypeAction; +use LightSaml\Action\Profile\Inbound\Message\DestinationValidatorResponseAction; +use LightSaml\Action\Profile\Inbound\Message\EntityIdFromMessageIssuerAction; +use LightSaml\Action\Profile\Inbound\Message\ResolvePartyEntityIdAction; +use LightSaml\Action\Profile\Inbound\Message\ReceiveMessageAction; +use LightSaml\Action\Profile\Inbound\Message\MessageSignatureValidatorAction; +use LightSaml\Action\Profile\Inbound\Response\AssertionAction; +use LightSaml\Action\Profile\Inbound\Response\DecryptAssertionsAction; +use LightSaml\Action\Profile\Inbound\Response\HasAssertionsValidatorAction; +use LightSaml\Action\Profile\Inbound\Response\HasAuthnStatementValidatorAction; +use LightSaml\Action\Profile\Inbound\Response\HasBearerAssertionsValidatorAction; +use LightSaml\Action\Profile\Inbound\Message\IssuerValidatorAction; +use LightSaml\Action\Profile\Inbound\Response\SpSsoStateAction; +use LightSaml\Action\Profile\Inbound\StatusResponse\InResponseToValidatorAction; +use LightSaml\Action\Profile\Inbound\StatusResponse\StatusAction; +use LightSaml\Build\Container\BuildContainerInterface; +use LightSaml\Builder\Action\ActionBuilderInterface; +use LightSaml\Builder\Action\Profile\AbstractProfileActionBuilder; +use LightSaml\SamlConstants; + +class SsoSpReceiveResponseActionBuilder extends AbstractProfileActionBuilder +{ + /** @var ActionBuilderInterface */ + private $assertionActionBuilder; + + /** + * @param BuildContainerInterface $buildContainer + * @param ActionBuilderInterface $assertionActionBuilder + */ + public function __construct(BuildContainerInterface $buildContainer, ActionBuilderInterface $assertionActionBuilder) + { + parent::__construct($buildContainer); + + $this->assertionActionBuilder = $assertionActionBuilder; + } + + /** + * @return void + */ + protected function doInitialize() + { + // Receive + $this->add(new ReceiveMessageAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getServiceContainer()->getBindingFactory() + ), 100); + $this->add(new AssertBindingTypeAction( + $this->buildContainer->getSystemContainer()->getLogger(), + array(SamlConstants::BINDING_SAML2_HTTP_POST) + )); + + // Response validation + $this->add(new IssuerValidatorAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getServiceContainer()->getNameIdValidator(), + SamlConstants::NAME_ID_FORMAT_ENTITY + ), 200); + $this->add(new EntityIdFromMessageIssuerAction( + $this->buildContainer->getSystemContainer()->getLogger() + )); + $this->add(new ResolvePartyEntityIdAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getPartyContainer()->getSpEntityDescriptorStore(), + $this->buildContainer->getPartyContainer()->getIdpEntityDescriptorStore(), + $this->buildContainer->getPartyContainer()->getTrustOptionsStore() + )); + $this->add(new InResponseToValidatorAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getStoreContainer()->getRequestStateStore() + )); + $this->add(new StatusAction( + $this->buildContainer->getSystemContainer()->getLogger() + )); + $this->add(new DestinationValidatorResponseAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getServiceContainer()->getEndpointResolver() + )); + $this->add(new MessageSignatureValidatorAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getServiceContainer()->getSignatureValidator() + )); + + $this->add(new DecryptAssertionsAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getServiceContainer()->getCredentialResolver() + )); + + $this->add(new HasAssertionsValidatorAction( + $this->buildContainer->getSystemContainer()->getLogger() + )); + $this->add(new HasAuthnStatementValidatorAction( + $this->buildContainer->getSystemContainer()->getLogger() + )); + $this->add(new HasBearerAssertionsValidatorAction( + $this->buildContainer->getSystemContainer()->getLogger() + )); + + $this->add(new AssertionAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->assertionActionBuilder->build() + )); + + $this->add(new FlushRequestStatesAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getStoreContainer()->getRequestStateStore() + )); + + $this->add(new SpSsoStateAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getServiceContainer()->getSessionProcessor() + )); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/Profile/SingleSignOn/Sp/SsoSpSendAuthnRequestActionBuilder.php b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/Profile/SingleSignOn/Sp/SsoSpSendAuthnRequestActionBuilder.php new file mode 100644 index 0000000000..d2ed20f991 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/Profile/SingleSignOn/Sp/SsoSpSendAuthnRequestActionBuilder.php @@ -0,0 +1,84 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Builder\Action\Profile\SingleSignOn\Sp; + +use LightSaml\Action\DispatchEventAction; +use LightSaml\Action\Profile\Outbound\AuthnRequest\CreateAuthnRequestAction; +use LightSaml\Action\Profile\Outbound\Message\CreateMessageIssuerAction; +use LightSaml\Action\Profile\Outbound\Message\DestinationAction; +use LightSaml\Action\Profile\Outbound\Message\MessageIdAction; +use LightSaml\Action\Profile\Outbound\Message\MessageIssueInstantAction; +use LightSaml\Action\Profile\Outbound\Message\ResolveEndpointIdpSsoAction; +use LightSaml\Action\Profile\Outbound\Message\SaveRequestStateAction; +use LightSaml\Action\Profile\Outbound\Message\SendMessageAction; +use LightSaml\Action\Profile\Outbound\Message\SetRelayStateAction; +use LightSaml\Action\Profile\Outbound\Message\SignMessageAction; +use LightSaml\Action\Profile\Outbound\Message\MessageVersionAction; +use LightSaml\Builder\Action\Profile\AbstractProfileActionBuilder; +use LightSaml\Event\Events; +use LightSaml\SamlConstants; + +class SsoSpSendAuthnRequestActionBuilder extends AbstractProfileActionBuilder +{ + /** + * @return void + */ + protected function doInitialize() + { + // Create AuthnRequest + $this->add(new ResolveEndpointIdpSsoAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getServiceContainer()->getEndpointResolver() + ), 100); + $this->add(new CreateAuthnRequestAction( + $this->buildContainer->getSystemContainer()->getLogger() + )); + $this->add(new SetRelayStateAction( + $this->buildContainer->getSystemContainer()->getLogger() + )); + $this->add(new MessageIdAction( + $this->buildContainer->getSystemContainer()->getLogger() + )); + $this->add(new MessageVersionAction( + $this->buildContainer->getSystemContainer()->getLogger(), + SamlConstants::VERSION_20 + )); + $this->add(new MessageIssueInstantAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getSystemContainer()->getTimeProvider() + )); + $this->add(new DestinationAction( + $this->buildContainer->getSystemContainer()->getLogger() + )); + $this->add(new CreateMessageIssuerAction( + $this->buildContainer->getSystemContainer()->getLogger() + )); + $this->add(new SaveRequestStateAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getStoreContainer()->getRequestStateStore() + )); + $this->add(new DispatchEventAction( + $this->buildContainer->getSystemContainer()->getEventDispatcher(), + Events::BEFORE_ENCRYPT + )); + $this->add(new SignMessageAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getServiceContainer()->getSignatureResolver() + )); + + // Send + $this->add(new SendMessageAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getServiceContainer()->getBindingFactory() + ), 400); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/Profile/SingleSignOn/Sp/SsoSpValidateAssertionActionBuilder.php b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/Profile/SingleSignOn/Sp/SsoSpValidateAssertionActionBuilder.php new file mode 100644 index 0000000000..7d6f7f5fa5 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Action/Profile/SingleSignOn/Sp/SsoSpValidateAssertionActionBuilder.php @@ -0,0 +1,68 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Builder\Action\Profile\SingleSignOn\Sp; + +use LightSaml\Action\Assertion\Inbound\AssertionSignatureValidatorAction; +use LightSaml\Action\Assertion\Inbound\AssertionValidatorAction; +use LightSaml\Action\Assertion\Inbound\AssertionIssuerFormatValidatorAction; +use LightSaml\Action\Assertion\Inbound\InResponseToValidatorAction; +use LightSaml\Action\Assertion\Inbound\KnownAssertionIssuerAction; +use LightSaml\Action\Assertion\Inbound\RecipientValidatorAction; +use LightSaml\Action\Assertion\Inbound\RepeatedIdValidatorAction; +use LightSaml\Action\Assertion\Inbound\TimeValidatorAction; +use LightSaml\Builder\Action\Profile\AbstractProfileActionBuilder; +use LightSaml\SamlConstants; + +class SsoSpValidateAssertionActionBuilder extends AbstractProfileActionBuilder +{ + /** + * @return void + */ + protected function doInitialize() + { + $this->add(new AssertionValidatorAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getServiceContainer()->getAssertionValidator() + ), 100); + $this->add(new AssertionIssuerFormatValidatorAction( + $this->buildContainer->getSystemContainer()->getLogger(), + SamlConstants::NAME_ID_FORMAT_ENTITY + )); + $this->add(new InResponseToValidatorAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getStoreContainer()->getRequestStateStore() + )); + $this->add(new KnownAssertionIssuerAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getPartyContainer()->getIdpEntityDescriptorStore() + )); + $this->add(new RecipientValidatorAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getServiceContainer()->getEndpointResolver() + )); + $this->add(new RepeatedIdValidatorAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getStoreContainer()->getIdStateStore() + )); + $this->add(new TimeValidatorAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getServiceContainer()->getAssertionTimeValidator(), + $this->buildContainer->getSystemContainer()->getTimeProvider(), + 120 + )); + $this->add(new AssertionSignatureValidatorAction( + $this->buildContainer->getSystemContainer()->getLogger(), + $this->buildContainer->getServiceContainer()->getSignatureValidator(), + true + )); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Context/ProfileContextBuilder.php b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Context/ProfileContextBuilder.php new file mode 100644 index 0000000000..d563289d94 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Context/ProfileContextBuilder.php @@ -0,0 +1,138 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Builder\Context; + +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Error\LightSamlBuildException; +use LightSaml\Provider\EntityDescriptor\EntityDescriptorProviderInterface; +use Symfony\Component\HttpFoundation\Request; + +class ProfileContextBuilder +{ + /** @var Request */ + private $request; + + /** @var EntityDescriptorProviderInterface */ + private $ownEntityDescriptorProvider; + + /** @var int */ + private $profileId; + + /** @var string */ + private $profileRole; + + /** + * @return Request + */ + public function getRequest() + { + return $this->request; + } + + /** + * @param Request $request + * + * @return ProfileContextBuilder + */ + public function setRequest(Request $request) + { + $this->request = $request; + + return $this; + } + + /** + * @return EntityDescriptorProviderInterface + */ + public function getOwnEntityDescriptorProvider() + { + return $this->ownEntityDescriptorProvider; + } + + /** + * @param EntityDescriptorProviderInterface $ownEntityDescriptorProvider + * + * @return ProfileContextBuilder + */ + public function setOwnEntityDescriptorProvider(EntityDescriptorProviderInterface $ownEntityDescriptorProvider) + { + $this->ownEntityDescriptorProvider = $ownEntityDescriptorProvider; + + return $this; + } + + /** + * @return int + */ + public function getProfileId() + { + return $this->profileId; + } + + /** + * @param int $profileId + * + * @return ProfileContextBuilder + */ + public function setProfileId($profileId) + { + $this->profileId = $profileId; + + return $this; + } + + /** + * @return string + */ + public function getProfileRole() + { + return $this->profileRole; + } + + /** + * @param string $profileRole + * + * @return ProfileContextBuilder + */ + public function setProfileRole($profileRole) + { + $this->profileRole = $profileRole; + + return $this; + } + + /** + * @return ProfileContext + */ + public function build() + { + if (null === $this->request) { + throw new LightSamlBuildException('HTTP Request not set'); + } + if (null === $this->ownEntityDescriptorProvider) { + throw new LightSamlBuildException('Own EntityDescriptor not set'); + } + if (null === $this->profileId) { + throw new LightSamlBuildException('ProfileID not set'); + } + if (null === $this->profileRole) { + throw new LightSamlBuildException('Profile role not set'); + } + + $result = new ProfileContext($this->profileId, $this->profileRole); + + $result->getHttpRequestContext()->setRequest($this->request); + $result->getOwnEntityContext()->setEntityDescriptor($this->ownEntityDescriptorProvider->get()); + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Builder/EntityDescriptor/SimpleEntityDescriptorBuilder.php b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/EntityDescriptor/SimpleEntityDescriptorBuilder.php new file mode 100644 index 0000000000..d5e5d9010d --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/EntityDescriptor/SimpleEntityDescriptorBuilder.php @@ -0,0 +1,180 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Builder\EntityDescriptor; + +use LightSaml\Model\Metadata\AssertionConsumerService; +use LightSaml\Model\Metadata\EntityDescriptor; +use LightSaml\Model\Metadata\IdpSsoDescriptor; +use LightSaml\Model\Metadata\KeyDescriptor; +use LightSaml\Model\Metadata\RoleDescriptor; +use LightSaml\Model\Metadata\SingleSignOnService; +use LightSaml\Model\Metadata\SpSsoDescriptor; +use LightSaml\Provider\EntityDescriptor\EntityDescriptorProviderInterface; +use LightSaml\SamlConstants; +use LightSaml\Credential\X509Certificate; + +class SimpleEntityDescriptorBuilder implements EntityDescriptorProviderInterface +{ + /** @var string */ + protected $entityId; + + /** @var string */ + protected $acsUrl; + + /** @var string[] */ + protected $acsBindings; + + /** @var string */ + protected $ssoUrl; + + /** @var string[] */ + protected $ssoBindings; + + /** @var string[]|null */ + protected $use; + + /** @var X509Certificate */ + protected $ownCertificate; + + /** @var EntityDescriptor */ + private $entityDescriptor; + + /** + * @param string $entityId + * @param string $acsUrl + * @param string $ssoUrl + * @param X509Certificate $ownCertificate + * @param string[] $acsBindings + * @param string[] $ssoBindings + * @param string[]|null $use + */ + public function __construct( + $entityId, + $acsUrl, + $ssoUrl, + X509Certificate $ownCertificate, + array $acsBindings = array(SamlConstants::BINDING_SAML2_HTTP_POST), + array $ssoBindings = array(SamlConstants::BINDING_SAML2_HTTP_POST, SamlConstants::BINDING_SAML2_HTTP_REDIRECT), + $use = array(KeyDescriptor::USE_ENCRYPTION, KeyDescriptor::USE_SIGNING) + ) { + $this->entityId = $entityId; + $this->acsUrl = $acsUrl; + $this->ssoUrl = $ssoUrl; + $this->ownCertificate = $ownCertificate; + $this->acsBindings = $acsBindings; + $this->ssoBindings = $ssoBindings; + $this->use = $use; + } + + /** + * @return EntityDescriptor + */ + public function get() + { + if (null === $this->entityDescriptor) { + $this->entityDescriptor = $this->getEntityDescriptor(); + if (false === $this->entityDescriptor instanceof EntityDescriptor) { + throw new \LogicException('Expected EntityDescriptor'); + } + } + + return $this->entityDescriptor; + } + + /** + * @return EntityDescriptor + */ + protected function getEntityDescriptor() + { + $entityDescriptor = new EntityDescriptor(); + $entityDescriptor->setEntityID($this->entityId); + + $spSsoDescriptor = $this->getSpSsoDescriptor(); + if ($spSsoDescriptor) { + $entityDescriptor->addItem($spSsoDescriptor); + } + + $idpSsoDescriptor = $this->getIdpSsoDescriptor(); + if ($idpSsoDescriptor) { + $entityDescriptor->addItem($idpSsoDescriptor); + } + + return $entityDescriptor; + } + + /** + * @return SpSsoDescriptor|null + */ + protected function getSpSsoDescriptor() + { + if (null === $this->acsUrl) { + return null; + } + + $spSso = new SpSsoDescriptor(); + + foreach ($this->acsBindings as $index => $biding) { + $acs = new AssertionConsumerService(); + $acs->setIndex($index)->setLocation($this->acsUrl)->setBinding($biding); + $spSso->addAssertionConsumerService($acs); + } + + $this->addKeyDescriptors($spSso); + + return $spSso; + } + + /** + * @return IdpSsoDescriptor + */ + protected function getIdpSsoDescriptor() + { + if (null === $this->ssoUrl) { + return null; + } + + $idpSso = new IdpSsoDescriptor(); + + foreach ($this->ssoBindings as $index => $binding) { + $sso = new SingleSignOnService(); + $sso + ->setLocation($this->ssoUrl) + ->setBinding($binding); + $idpSso->addSingleSignOnService($sso); + } + + $this->addKeyDescriptors($idpSso); + + return $idpSso; + } + + /** + * @param RoleDescriptor $descriptor + */ + protected function addKeyDescriptors(RoleDescriptor $descriptor) + { + if ($this->use) { + foreach ($this->use as $use) { + $kd = new KeyDescriptor(); + $kd->setUse($use); + $kd->setCertificate($this->ownCertificate); + + $descriptor->addKeyDescriptor($kd); + } + } else { + $kd = new KeyDescriptor(); + $kd->setCertificate($this->ownCertificate); + + $descriptor->addKeyDescriptor($kd); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/AbstractProfileBuilder.php b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/AbstractProfileBuilder.php new file mode 100644 index 0000000000..42a3ca187c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/AbstractProfileBuilder.php @@ -0,0 +1,68 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Builder\Profile; + +use LightSaml\Build\Container\BuildContainerInterface; +use LightSaml\Builder\Context\ProfileContextBuilder; + +abstract class AbstractProfileBuilder implements ProfileBuilderInterface +{ + /** @var BuildContainerInterface */ + protected $container; + + /** + * @param BuildContainerInterface $buildContainer + */ + public function __construct(BuildContainerInterface $buildContainer) + { + $this->container = $buildContainer; + } + + /** + * @return \LightSaml\Action\CompositeAction + */ + public function buildAction() + { + return $this->getActionBuilder()->build(); + } + + /** + * @return \LightSaml\Context\Profile\ProfileContext + */ + public function buildContext() + { + $builder = new ProfileContextBuilder(); + $builder + ->setProfileId($this->getProfileId()) + ->setRequest($this->container->getSystemContainer()->getRequest()) + ->setProfileRole($this->getProfileRole()) + ->setOwnEntityDescriptorProvider($this->container->getOwnContainer()->getOwnEntityDescriptorProvider()) + ; + + return $builder->build(); + } + + /** + * @return string + */ + abstract protected function getProfileId(); + + /** + * @return string + */ + abstract protected function getProfileRole(); + + /** + * @return \LightSaml\Builder\Action\ActionBuilderInterface + */ + abstract protected function getActionBuilder(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/Metadata/MetadataProfileBuilder.php b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/Metadata/MetadataProfileBuilder.php new file mode 100644 index 0000000000..bb2cb383bd --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/Metadata/MetadataProfileBuilder.php @@ -0,0 +1,44 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Builder\Profile\Metadata; + +use LightSaml\Builder\Action\Profile\Metadata\MetadataActionBuilder; +use LightSaml\Builder\Profile\AbstractProfileBuilder; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Profile\Profiles; + +class MetadataProfileBuilder extends AbstractProfileBuilder +{ + /** + * @return string + */ + protected function getProfileId() + { + return Profiles::METADATA; + } + + /** + * @return string + */ + protected function getProfileRole() + { + return ProfileContext::ROLE_NONE; + } + + /** + * @return \LightSaml\Builder\Action\ActionBuilderInterface + */ + protected function getActionBuilder() + { + return new MetadataActionBuilder($this->container); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/ProfileBuilderInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/ProfileBuilderInterface.php new file mode 100644 index 0000000000..d061bcfb75 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/ProfileBuilderInterface.php @@ -0,0 +1,25 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Builder\Profile; + +interface ProfileBuilderInterface +{ + /** + * @return \LightSaml\Action\CompositeAction + */ + public function buildAction(); + + /** + * @return \LightSaml\Context\Profile\ProfileContext + */ + public function buildContext(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/WebBrowserSso/Sp/SsoSpReceiveResponseProfileBuilder.php b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/WebBrowserSso/Sp/SsoSpReceiveResponseProfileBuilder.php new file mode 100644 index 0000000000..ff2f48ac46 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/WebBrowserSso/Sp/SsoSpReceiveResponseProfileBuilder.php @@ -0,0 +1,50 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Builder\Profile\WebBrowserSso\Sp; + +use LightSaml\Builder\Action\Profile\SingleSignOn\Sp\SsoSpReceiveResponseActionBuilder; +use LightSaml\Builder\Action\Profile\SingleSignOn\Sp\SsoSpValidateAssertionActionBuilder; +use LightSaml\Builder\Profile\AbstractProfileBuilder; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Profile\Profiles; + +class SsoSpReceiveResponseProfileBuilder extends AbstractProfileBuilder +{ + /** + * @return string + */ + protected function getProfileId() + { + return Profiles::SSO_SP_RECEIVE_RESPONSE; + } + + /** + * @return string + */ + protected function getProfileRole() + { + return ProfileContext::ROLE_SP; + } + + /** + * @return \LightSaml\Builder\Action\ActionBuilderInterface + */ + protected function getActionBuilder() + { + $result = new SsoSpReceiveResponseActionBuilder( + $this->container, + new SsoSpValidateAssertionActionBuilder($this->container) + ); + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/WebBrowserSso/Sp/SsoSpSendAuthnRequestProfileBuilder.php b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/WebBrowserSso/Sp/SsoSpSendAuthnRequestProfileBuilder.php new file mode 100644 index 0000000000..79228b4e97 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/WebBrowserSso/Sp/SsoSpSendAuthnRequestProfileBuilder.php @@ -0,0 +1,97 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Builder\Profile\WebBrowserSso\Sp; + +use LightSaml\Build\Container\BuildContainerInterface; +use LightSaml\Builder\Action\Profile\SingleSignOn\Sp\SsoSpSendAuthnRequestActionBuilder; +use LightSaml\Builder\Profile\AbstractProfileBuilder; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Meta\TrustOptions\TrustOptions; +use LightSaml\Model\Metadata\EntityDescriptor; +use LightSaml\Profile\Profiles; + +class SsoSpSendAuthnRequestProfileBuilder extends AbstractProfileBuilder +{ + protected $idpEntityId; + + /** + * @param BuildContainerInterface $buildContainer + * @param string $idpEntityId + */ + public function __construct(BuildContainerInterface $buildContainer, $idpEntityId) + { + parent::__construct($buildContainer); + + $this->idpEntityId = $idpEntityId; + } + + public function buildContext() + { + $result = parent::buildContext(); + + $idpEd = $this->container->getPartyContainer()->getIdpEntityDescriptorStore()->get($this->idpEntityId); + if (false == $idpEd) { + throw new \RuntimeException(sprintf('Unknown IDP "%s"', $this->idpEntityId)); + } + + $trustOptions = $this->getTrustOptions($idpEd); + + $result->getPartyEntityContext() + ->setEntityDescriptor($idpEd) + ->setTrustOptions($trustOptions) + ; + + return $result; + } + + /** + * @return string + */ + protected function getProfileId() + { + return Profiles::SSO_SP_SEND_AUTHN_REQUEST; + } + + /** + * @return string + */ + protected function getProfileRole() + { + return ProfileContext::ROLE_SP; + } + + /** + * @return \LightSaml\Builder\Action\ActionBuilderInterface + */ + protected function getActionBuilder() + { + return new SsoSpSendAuthnRequestActionBuilder($this->container); + } + + /** + * @param EntityDescriptor $idpEd + * + * @return TrustOptions + */ + private function getTrustOptions(EntityDescriptor $idpEd) + { + $trustOptions = $this->container->getPartyContainer()->getTrustOptionsStore()->get($this->idpEntityId) ?: new TrustOptions(); + + $wantAuthnRequestsSigned = $idpEd->getFirstIdpSsoDescriptor()->getWantAuthnRequestsSigned(); + + if (null !== $wantAuthnRequestsSigned) { + $trustOptions->setSignAuthnRequest($wantAuthnRequestsSigned); + } + + return $trustOptions; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/WebBrowserSso/Sp/SsoSpSendAuthnRequestProfileBuilderFactory.php b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/WebBrowserSso/Sp/SsoSpSendAuthnRequestProfileBuilderFactory.php new file mode 100644 index 0000000000..93f46ef84d --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Builder/Profile/WebBrowserSso/Sp/SsoSpSendAuthnRequestProfileBuilderFactory.php @@ -0,0 +1,38 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Builder\Profile\WebBrowserSso\Sp; + +use LightSaml\Build\Container\BuildContainerInterface; + +class SsoSpSendAuthnRequestProfileBuilderFactory +{ + /** @var BuildContainerInterface */ + private $buildContainer; + + /** + * @param BuildContainerInterface $buildContainer + */ + public function __construct(BuildContainerInterface $buildContainer) + { + $this->buildContainer = $buildContainer; + } + + /** + * @param string $idpEntityId + * + * @return SsoSpSendAuthnRequestProfileBuilder + */ + public function get($idpEntityId) + { + return new SsoSpSendAuthnRequestProfileBuilder($this->buildContainer, $idpEntityId); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/ClaimTypes.php b/vendor/lightsaml/lightsaml/src/LightSaml/ClaimTypes.php new file mode 100644 index 0000000000..5dfdddb024 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/ClaimTypes.php @@ -0,0 +1,31 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml; + +class ClaimTypes +{ + const COMMON_NAME = 'http://schemas.xmlsoap.org/claims/CommonName'; + const EMAIL_ADDRESS = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'; + const GIVEN_NAME = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname'; + const NAME = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name'; + const UPN = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'; + const ADFS_1_EMAIL = 'http://schemas.xmlsoap.org/claims/EmailAddress'; + const GROUP = 'http://schemas.xmlsoap.org/claims/Group'; + const ADFS_1_UPN = 'http://schemas.xmlsoap.org/claims/UPN'; + const ROLE = 'http://schemas.microsoft.com/ws/2008/06/identity/claims/role'; + const SURNAME = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname'; + const PPID = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier'; + const NAME_ID = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier'; + const AUTHENTICATION_TIMESTAMP = 'http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationinstant'; + const AUTHENTICATION_METHOD = 'http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod'; + const WINDOWS_ACCOUNT_NAME = 'http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname'; +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/AbstractContext.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/AbstractContext.php new file mode 100644 index 0000000000..5138cb01e4 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/AbstractContext.php @@ -0,0 +1,235 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context; + +abstract class AbstractContext implements ContextInterface +{ + /** @var ContextInterface|null */ + private $parent; + + /** @var ContextInterface[] */ + private $subContexts = array(); + + /** + * @return ContextInterface|null + */ + public function getParent() + { + return $this->parent; + } + + /** + * @return ContextInterface + */ + public function getTopParent() + { + if ($this->getParent()) { + return $this->getParent()->getTopParent(); + } + + return $this; + } + + /** + * @param ContextInterface|null $parent + * + * @return ContextInterface + */ + public function setParent(ContextInterface $parent = null) + { + $this->parent = $parent; + + return $this; + } + + /** + * @param string $name + * @param null|string $class + * + * @return ContextInterface|null + */ + public function getSubContext($name, $class = null) + { + if (isset($this->subContexts[$name])) { + return $this->subContexts[$name]; + } + + if ($class) { + $result = $this->createSubContext($class); + $this->addSubContext($name, $result); + + return $result; + } + + return null; + } + + /** + * @param string $class + * @param bool $autoCreate + * + * @return ContextInterface|null + */ + public function getSubContextByClass($class, $autoCreate) + { + return $this->getSubContext($class, $autoCreate ? $class : null); + } + + /** + * @param string $name + * @param object|ContextInterface $subContext + * + * @return AbstractContext + */ + public function addSubContext($name, $subContext) + { + if (false === is_object($subContext)) { + throw new \InvalidArgumentException('Expected object or ContextInterface'); + } + + $existing = isset($this->subContexts[$name]) ? $this->subContexts[$name] : null; + if ($existing === $subContext) { + return $this; + } + + $this->subContexts[$name] = $subContext; + if ($subContext instanceof ContextInterface) { + $subContext->setParent($this); + } + + if ($existing instanceof ContextInterface) { + $existing->setParent(null); + } + + return $this; + } + + /** + * @param string $name + * + * @return ContextInterface + */ + public function removeSubContext($name) + { + $subContext = $this->getSubContext($name, false); + + if ($subContext) { + $subContext->setParent(null); + unset($this->subContexts[$name]); + } + + return $this; + } + + /** + * @param string $name + * + * @return bool + */ + public function containsSubContext($name) + { + return isset($this->subContexts[$name]); + } + + /** + * @return ContextInterface + */ + public function clearSubContexts() + { + foreach ($this->subContexts as $subContext) { + $subContext->setParent(null); + } + $this->subContexts = array(); + + return $this; + } + + /** + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->subContexts); + } + + /** + * @param string $ownName + * + * @return array + */ + public function debugPrintTree($ownName = 'root') + { + $result = array( + $ownName => static::class, + ); + + if ($this->subContexts) { + $arr = array(); + foreach ($this->subContexts as $name => $subContext) { + if ($subContext instanceof ContextInterface) { + $arr = array_merge($arr, $subContext->debugPrintTree($name)); + } else { + $arr = array_merge($arr, array($name => get_class($subContext))); + } + } + $result[$ownName.'__children'] = $arr; + } + + return $result; + } + + /** + * @return string + */ + public function __toString() + { + return json_encode($this->debugPrintTree(), JSON_PRETTY_PRINT); + } + + /** + * @param string $path + * + * @return ContextInterface + */ + public function getPath($path) + { + if (is_string($path)) { + $path = explode('/', $path); + } elseif (false === is_array($path)) { + throw new \InvalidArgumentException('Expected string or array'); + } + + $name = array_shift($path); + $subContext = $this->getSubContext($name); + if (null == $subContext) { + return null; + } + + if (empty($path)) { + return $subContext; + } else { + return $subContext->getPath($path); + } + } + + /** + * @param string $class + * + * @return ContextInterface + */ + protected function createSubContext($class) + { + $result = new $class(); + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/ContextInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/ContextInterface.php new file mode 100644 index 0000000000..89c6541e04 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/ContextInterface.php @@ -0,0 +1,87 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context; + +interface ContextInterface extends \IteratorAggregate +{ + /** + * @return ContextInterface|null + */ + public function getParent(); + + /** + * @return ContextInterface + */ + public function getTopParent(); + + /** + * @param ContextInterface|null $parent + * + * @return ContextInterface + */ + public function setParent(ContextInterface $parent = null); + + /** + * @param string $name + * @param null|string $class + * + * @return ContextInterface|null + */ + public function getSubContext($name, $class = null); + + /** + * @param string $class + * @param bool $autoCreate + * + * @return ContextInterface|null + */ + public function getSubContextByClass($class, $autoCreate); + + /** + * @param string $name + * @param object|ContextInterface $subContext + */ + public function addSubContext($name, $subContext); + + /** + * @param string $name + * + * @return ContextInterface + */ + public function removeSubContext($name); + + /** + * @param string $name + * + * @return bool + */ + public function containsSubContext($name); + + /** + * @return ContextInterface + */ + public function clearSubContexts(); + + /** + * @param string $ownName + * + * @return array + */ + public function debugPrintTree($ownName = 'root'); + + /** + * @param string $path + * + * @return ContextInterface + */ + public function getPath($path); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/AbstractProfileContext.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/AbstractProfileContext.php new file mode 100644 index 0000000000..58c861dcd3 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/AbstractProfileContext.php @@ -0,0 +1,35 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context\Profile; + +use LightSaml\Context\AbstractContext; +use LightSaml\Error\LightSamlContextException; + +abstract class AbstractProfileContext extends AbstractContext +{ + /** + * @return ProfileContext + */ + public function getProfileContext() + { + $result = $this; + while ($result && false == $result instanceof ProfileContext) { + $result = $result->getParent(); + } + + if ($result) { + return $result; + } + + throw new LightSamlContextException($this, 'Missing ProfileContext'); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/AssertionContext.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/AssertionContext.php new file mode 100644 index 0000000000..9c46dc7625 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/AssertionContext.php @@ -0,0 +1,87 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context\Profile; + +use LightSaml\Model\Assertion\Assertion; +use LightSaml\Model\Assertion\EncryptedElement; + +class AssertionContext extends AbstractProfileContext +{ + /** @var Assertion|null */ + private $assertion; + + /** @var EncryptedElement|null */ + private $encryptedAssertion; + + /** @var string */ + private $id; + + /** + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * @param string $id + * + * @return AssertionContext + */ + public function setId($id) + { + $this->id = $id; + + return $this; + } + + /** + * @return Assertion|null + */ + public function getAssertion() + { + return $this->assertion; + } + + /** + * @param Assertion $assertion + * + * @return AssertionContext + */ + public function setAssertion(Assertion $assertion = null) + { + $this->assertion = $assertion; + + return $this; + } + + /** + * @return EncryptedElement|null + */ + public function getEncryptedAssertion() + { + return $this->encryptedAssertion; + } + + /** + * @param EncryptedElement $encryptedAssertion + * + * @return AssertionContext + */ + public function setEncryptedAssertion(EncryptedElement $encryptedAssertion = null) + { + $this->encryptedAssertion = $encryptedAssertion; + + return $this; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/EndpointContext.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/EndpointContext.php new file mode 100644 index 0000000000..d1e18f4b99 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/EndpointContext.php @@ -0,0 +1,40 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context\Profile; + +use LightSaml\Model\Metadata\Endpoint; + +class EndpointContext extends AbstractProfileContext +{ + /** @var Endpoint */ + private $endpoint; + + /** + * @return Endpoint|null + */ + public function getEndpoint() + { + return $this->endpoint; + } + + /** + * @param Endpoint $endpoint + * + * @return EndpointContext + */ + public function setEndpoint(Endpoint $endpoint) + { + $this->endpoint = $endpoint; + + return $this; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/EntityContext.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/EntityContext.php new file mode 100644 index 0000000000..1a7b466805 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/EntityContext.php @@ -0,0 +1,87 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context\Profile; + +use LightSaml\Meta\TrustOptions\TrustOptions; +use LightSaml\Model\Metadata\EntityDescriptor; + +class EntityContext extends AbstractProfileContext +{ + /** @var string */ + private $entityId; + + /** @var EntityDescriptor */ + private $entityDescriptor; + + /** @var TrustOptions */ + private $trustOptions; + + /** + * @return string + */ + public function getEntityId() + { + return $this->entityId; + } + + /** + * @param string $entityId + * + * @return EntityContext + */ + public function setEntityId($entityId) + { + $this->entityId = $entityId; + + return $this; + } + + /** + * @return EntityDescriptor + */ + public function getEntityDescriptor() + { + return $this->entityDescriptor; + } + + /** + * @param EntityDescriptor $entityDescriptor + * + * @return EntityContext + */ + public function setEntityDescriptor(EntityDescriptor $entityDescriptor) + { + $this->entityDescriptor = $entityDescriptor; + + return $this; + } + + /** + * @return TrustOptions + */ + public function getTrustOptions() + { + return $this->trustOptions; + } + + /** + * @param TrustOptions $trustOptions + * + * @return EntityContext + */ + public function setTrustOptions(TrustOptions $trustOptions) + { + $this->trustOptions = $trustOptions; + + return $this; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/ExceptionContext.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/ExceptionContext.php new file mode 100644 index 0000000000..9a0f7fc194 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/ExceptionContext.php @@ -0,0 +1,79 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context\Profile; + +class ExceptionContext extends AbstractProfileContext +{ + /** @var \Exception */ + protected $exception; + + /** @var ExceptionContext|null */ + protected $nextExceptionContext; + + /** + * @param \Exception|null $exception + */ + public function __construct(\Exception $exception = null) + { + $this->exception = $exception; + } + + /** + * @return \Exception + */ + public function getException() + { + return $this->exception; + } + + /** + * @return \Exception|null + */ + public function getLastException() + { + if (null == $this->nextExceptionContext) { + return $this->exception; + } + + return $this->nextExceptionContext->getException(); + } + + /** + * @return ExceptionContext|null + */ + public function getNextExceptionContext() + { + return $this->nextExceptionContext; + } + + /** + * @param \Exception $exception + * + * @return ExceptionContext + */ + public function addException(\Exception $exception) + { + if ($this->exception) { + if (null == $this->nextExceptionContext) { + $this->nextExceptionContext = new self($exception); + + return $this->nextExceptionContext; + } else { + return $this->nextExceptionContext->addException($exception); + } + } else { + $this->exception = $exception; + } + + return $this; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/Helper/AssertionContextHelper.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/Helper/AssertionContextHelper.php new file mode 100644 index 0000000000..40136754bd --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/Helper/AssertionContextHelper.php @@ -0,0 +1,34 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context\Profile\Helper; + +use LightSaml\Context\Profile\AssertionContext; +use LightSaml\Error\LightSamlContextException; +use LightSaml\Model\Assertion\EncryptedAssertionReader; + +abstract class AssertionContextHelper +{ + /** + * @param AssertionContext $context + * + * @return EncryptedAssertionReader + */ + public static function getEncryptedAssertionReader(AssertionContext $context) + { + $result = $context->getEncryptedAssertion(); + if ($result instanceof EncryptedAssertionReader) { + return $result; + } + + throw new LightSamlContextException($context, 'Expected EncryptedAssertionReader'); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/Helper/LogHelper.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/Helper/LogHelper.php new file mode 100644 index 0000000000..4a1936df70 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/Helper/LogHelper.php @@ -0,0 +1,74 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context\Profile\Helper; + +use LightSaml\Action\ActionInterface; +use LightSaml\Context\ContextInterface; +use LightSaml\Context\Profile\ProfileContext; + +abstract class LogHelper +{ + /** + * @param ContextInterface $context + * @param ActionInterface $action + * @param array $extraData + * + * @return array + */ + public static function getActionContext(ContextInterface $context, ActionInterface $action, array $extraData = null) + { + return self::getContext($context, $action, $extraData, false); + } + + /** + * @param ContextInterface $context + * @param ActionInterface $action + * @param array $extraData + * + * @return array + */ + public static function getActionErrorContext(ContextInterface $context, ActionInterface $action, array $extraData = null) + { + return self::getContext($context, $action, $extraData, true); + } + + /** + * @param ContextInterface $context + * @param ActionInterface $action + * @param array $extraData + * @param bool $logWholeContext + * + * @return array + */ + private static function getContext(ContextInterface $context, ActionInterface $action = null, array $extraData = null, $logWholeContext = false) + { + $topContext = $context->getTopParent(); + $result = array(); + if ($topContext instanceof ProfileContext) { + $result['profile_id'] = $topContext->getProfileId(); + $result['own_role'] = $topContext->getOwnRole(); + } + if ($action) { + $result['action'] = get_class($action); + } + $result['top_context_id'] = spl_object_hash($topContext); + + if ($logWholeContext) { + $result['top_context'] = $topContext; + } + if ($extraData) { + $result = array_merge($result, $extraData); + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/Helper/MessageContextHelper.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/Helper/MessageContextHelper.php new file mode 100644 index 0000000000..0c578fbace --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/Helper/MessageContextHelper.php @@ -0,0 +1,129 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context\Profile\Helper; + +use LightSaml\Context\Profile\MessageContext; +use LightSaml\Error\LightSamlContextException; +use LightSaml\Model\Protocol\AbstractRequest; +use LightSaml\Model\Protocol\AuthnRequest; +use LightSaml\Model\Protocol\LogoutRequest; +use LightSaml\Model\Protocol\LogoutResponse; +use LightSaml\Model\Protocol\Response; +use LightSaml\Model\Protocol\StatusResponse; + +abstract class MessageContextHelper +{ + /** + * @param MessageContext $context + * + * @return \LightSaml\Model\Protocol\SamlMessage + */ + public static function asSamlMessage(MessageContext $context) + { + $message = $context->getMessage(); + if ($message) { + return $message; + } + + throw new LightSamlContextException($context, 'Missing SamlMessage'); + } + + /** + * @param MessageContext $context + * + * @return \LightSaml\Model\Protocol\AuthnRequest + */ + public static function asAuthnRequest(MessageContext $context) + { + $message = $context->getMessage(); + if ($message instanceof AuthnRequest) { + return $message; + } + + throw new LightSamlContextException($context, 'Expected AuthnRequest'); + } + + /** + * @param MessageContext $context + * + * @return \LightSaml\Model\Protocol\AbstractRequest + */ + public static function asAbstractRequest(MessageContext $context) + { + $message = $context->getMessage(); + if ($message instanceof AbstractRequest) { + return $message; + } + + throw new LightSamlContextException($context, 'Expected AbstractRequest'); + } + + /** + * @param MessageContext $context + * + * @return \LightSaml\Model\Protocol\Response + */ + public static function asResponse(MessageContext $context) + { + $message = $context->getMessage(); + if ($message instanceof Response) { + return $message; + } + + throw new LightSamlContextException($context, 'Expected Response'); + } + + /** + * @param MessageContext $context + * + * @return \LightSaml\Model\Protocol\StatusResponse + */ + public static function asStatusResponse(MessageContext $context) + { + $message = $context->getMessage(); + if ($message instanceof StatusResponse) { + return $message; + } + + throw new LightSamlContextException($context, 'Expected StatusResponse'); + } + + /** + * @param MessageContext $context + * + * @return \LightSaml\Model\Protocol\LogoutRequest + */ + public static function asLogoutRequest(MessageContext $context) + { + $message = $context->getMessage(); + if ($message instanceof LogoutRequest) { + return $message; + } + + throw new LightSamlContextException($context, 'Expected LogoutRequest'); + } + + /** + * @param MessageContext $context + * + * @return \LightSaml\Model\Protocol\LogoutResponse + */ + public static function asLogoutResponse(MessageContext $context) + { + $message = $context->getMessage(); + if ($message instanceof LogoutResponse) { + return $message; + } + + throw new LightSamlContextException($context, 'Expected LogoutResponse'); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/HttpRequestContext.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/HttpRequestContext.php new file mode 100644 index 0000000000..b25445d14c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/HttpRequestContext.php @@ -0,0 +1,40 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context\Profile; + +use Symfony\Component\HttpFoundation\Request; + +class HttpRequestContext extends AbstractProfileContext +{ + /** @var Request */ + private $request; + + /** + * @return Request|null + */ + public function getRequest() + { + return $this->request; + } + + /** + * @param Request $request + * + * @return HttpRequestContext + */ + public function setRequest(Request $request) + { + $this->request = $request; + + return $this; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/HttpResponseContext.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/HttpResponseContext.php new file mode 100644 index 0000000000..20ac3c6e5e --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/HttpResponseContext.php @@ -0,0 +1,40 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context\Profile; + +use Symfony\Component\HttpFoundation\Response; + +class HttpResponseContext extends AbstractProfileContext +{ + /** @var Response */ + private $response; + + /** + * @return Response|null + */ + public function getResponse() + { + return $this->response; + } + + /** + * @param Response $response + * + * @return HttpResponseContext + */ + public function setResponse(Response $response) + { + $this->response = $response; + + return $this; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/LogoutContext.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/LogoutContext.php new file mode 100644 index 0000000000..d3d95be3a0 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/LogoutContext.php @@ -0,0 +1,64 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context\Profile; + +use LightSaml\State\Sso\SsoSessionState; + +class LogoutContext extends AbstractProfileContext +{ + /** @var SsoSessionState|null */ + protected $ssoSessionState; + + /** @var bool */ + protected $allSsoSessionsTerminated = false; + + /** + * @return SsoSessionState|null + */ + public function getSsoSessionState() + { + return $this->ssoSessionState; + } + + /** + * @param SsoSessionState $ssoSessionState + * + * @return LogoutContext + */ + public function setSsoSessionState(SsoSessionState $ssoSessionState) + { + $this->ssoSessionState = $ssoSessionState; + $this->allSsoSessionsTerminated = false; + + return $this; + } + + /** + * @return bool + */ + public function areAllSsoSessionsTerminated() + { + return $this->allSsoSessionsTerminated; + } + + /** + * @param bool $allSsoSessionsTerminated + * + * @return LogoutContext + */ + public function setAllSsoSessionsTerminated($allSsoSessionsTerminated) + { + $this->allSsoSessionsTerminated = (bool) $allSsoSessionsTerminated; + + return $this; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/MessageContext.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/MessageContext.php new file mode 100644 index 0000000000..025c6d2f6b --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/MessageContext.php @@ -0,0 +1,133 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context\Profile; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\Protocol\AuthnRequest; +use LightSaml\Model\Protocol\LogoutRequest; +use LightSaml\Model\Protocol\LogoutResponse; +use LightSaml\Model\Protocol\Response; +use LightSaml\Model\Protocol\SamlMessage; + +class MessageContext extends AbstractProfileContext +{ + /** @var SamlMessage */ + private $message; + + /** @var string */ + private $bindingType; + + /** + * @return string + */ + public function getBindingType() + { + return $this->bindingType; + } + + /** + * @param string $bindingType + * + * @return MessageContext + */ + public function setBindingType($bindingType) + { + $this->bindingType = $bindingType; + + return $this; + } + + /** + * @return SamlMessage|null + */ + public function getMessage() + { + return $this->message; + } + + /** + * @param SamlMessage|null $message + * + * @return MessageContext + */ + public function setMessage(SamlMessage $message = null) + { + $this->message = $message; + + return $this; + } + + /** + * @return AuthnRequest|null + */ + public function asAuthnRequest() + { + if ($this->message instanceof AuthnRequest) { + return $this->message; + } + + return null; + } + + /** + * @return LogoutRequest|null + */ + public function asLogoutRequest() + { + if ($this->message instanceof LogoutRequest) { + return $this->message; + } + + return null; + } + + /** + * @return Response|null + */ + public function asResponse() + { + if ($this->message instanceof Response) { + return $this->message; + } + + return null; + } + + /** + * @return LogoutResponse|null + */ + public function asLogoutResponse() + { + if ($this->message instanceof LogoutResponse) { + return $this->message; + } + + return null; + } + + /** + * @return SerializationContext + */ + public function getSerializationContext() + { + return $this->getSubContext(ProfileContexts::SERIALIZATION, SerializationContext::class); + } + + /** + * @return DeserializationContext + */ + public function getDeserializationContext() + { + return $this->getSubContext(ProfileContexts::DESERIALIZATION, DeserializationContext::class); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/ProfileContext.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/ProfileContext.php new file mode 100644 index 0000000000..d73dfb6f37 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/ProfileContext.php @@ -0,0 +1,241 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context\Profile; + +use LightSaml\Error\LightSamlContextException; + +class ProfileContext extends AbstractProfileContext +{ + const ROLE_SP = 'sp'; + const ROLE_IDP = 'idp'; + const ROLE_NONE = 'none'; + + /** @var string */ + private $profileId; + + /** @var string */ + private $ownRole; + + /** @var string */ + private $relayState; + + /** + * @param string $profileId + * @param string $ownRole + */ + public function __construct($profileId, $ownRole) + { + $this->profileId = $profileId; + $this->ownRole = $ownRole; + } + + /** + * @return string + */ + public function getProfileId() + { + return $this->profileId; + } + + /** + * @return string + */ + public function getOwnRole() + { + return $this->ownRole; + } + + /** + * @return string + */ + public function getRelayState() + { + return $this->relayState; + } + + /** + * @param string $relayState + * + * @return ProfileContext + */ + public function setRelayState($relayState) + { + $this->relayState = $relayState; + + return $this; + } + + /** + * @return MessageContext + */ + public function getInboundContext() + { + return $this->getSubContext(ProfileContexts::INBOUND_MESSAGE, MessageContext::class); + } + + /** + * @return MessageContext + */ + public function getOutboundContext() + { + return $this->getSubContext(ProfileContexts::OUTBOUND_MESSAGE, MessageContext::class); + } + + /** + * @return HttpRequestContext + */ + public function getHttpRequestContext() + { + return $this->getSubContext(ProfileContexts::HTTP_REQUEST, HttpRequestContext::class); + } + + /** + * @return HttpResponseContext + */ + public function getHttpResponseContext() + { + return $this->getSubContext(ProfileContexts::HTTP_RESPONSE, HttpResponseContext::class); + } + + /** + * @return EntityContext + */ + public function getOwnEntityContext() + { + return $this->getSubContext(ProfileContexts::OWN_ENTITY, EntityContext::class); + } + + /** + * @return EntityContext + */ + public function getPartyEntityContext() + { + return $this->getSubContext(ProfileContexts::PARTY_ENTITY, EntityContext::class); + } + + /** + * @return EndpointContext + */ + public function getEndpointContext() + { + return $this->getSubContext(ProfileContexts::ENDPOINT, EndpointContext::class); + } + + /** + * @return LogoutContext + */ + public function getLogoutContext() + { + return $this->getSubContext(ProfileContexts::LOGOUT, LogoutContext::class); + } + + /** + * @return \Symfony\Component\HttpFoundation\Request + */ + public function getHttpRequest() + { + $httpRequestContext = $this->getHttpRequestContext(); + if (null === $httpRequestContext->getRequest()) { + throw new LightSamlContextException($this, 'Missing Request in HTTP request context'); + } + + return $httpRequestContext->getRequest(); + } + + /** + * @return \LightSaml\Model\Protocol\SamlMessage + */ + public function getInboundMessage() + { + $inboundContext = $this->getInboundContext(); + if (null === $inboundContext->getMessage()) { + throw new LightSamlContextException($this, 'Missing message in inbound context'); + } + + return $inboundContext->getMessage(); + } + + /** + * @return \LightSaml\Model\Protocol\SamlMessage + */ + public function getOutboundMessage() + { + $outboundContext = $this->getOutboundContext(); + if (null === $outboundContext->getMessage()) { + throw new LightSamlContextException($this, 'Missing message in outbound context'); + } + + return $outboundContext->getMessage(); + } + + /** + * @return \LightSaml\Model\Metadata\Endpoint + */ + public function getEndpoint() + { + $endpointContext = $this->getEndpointContext(); + if (null === $endpointContext->getEndpoint()) { + throw new LightSamlContextException($this, 'Missing Endpoint in endpoint context'); + } + + return $endpointContext->getEndpoint(); + } + + /** + * @return \LightSaml\Model\Metadata\EntityDescriptor + */ + public function getOwnEntityDescriptor() + { + $ownEntityContext = $this->getOwnEntityContext(); + if (null === $ownEntityContext->getEntityDescriptor()) { + throw new LightSamlContextException($this, 'Missing EntityDescriptor in own entity context'); + } + + return $ownEntityContext->getEntityDescriptor(); + } + + /** + * @return \LightSaml\Model\Metadata\EntityDescriptor + */ + public function getPartyEntityDescriptor() + { + $partyEntityContext = $this->getPartyEntityContext(); + if (null === $partyEntityContext->getEntityDescriptor()) { + throw new LightSamlContextException($this, 'Missing EntityDescriptor in party entity context'); + } + + return $partyEntityContext->getEntityDescriptor(); + } + + /** + * @return \LightSaml\Meta\TrustOptions\TrustOptions + */ + public function getTrustOptions() + { + $partyEntityContext = $this->getPartyEntityContext(); + if (null === $partyEntityContext->getTrustOptions()) { + throw new LightSamlContextException($this, 'Missing TrustOptions in party entity context'); + } + + return $partyEntityContext->getTrustOptions(); + } + + public function getLogoutSsoSessionState() + { + $logoutContext = $this->getLogoutContext(); + if (null == $logoutContext->getSsoSessionState()) { + throw new LightSamlContextException($this, 'Missing SsoSessionState in logout context'); + } + + return $logoutContext->getSsoSessionState(); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/ProfileContexts.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/ProfileContexts.php new file mode 100644 index 0000000000..344ea01b5b --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/ProfileContexts.php @@ -0,0 +1,28 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context\Profile; + +class ProfileContexts +{ + const INBOUND_MESSAGE = 'inbound_message'; + const OUTBOUND_MESSAGE = 'outbound_message'; + const OWN_ENTITY = 'own_entity'; + const PARTY_ENTITY = 'party_entity'; + const DESERIALIZATION = 'deserialization'; + const SERIALIZATION = 'serialization'; + const HTTP_REQUEST = 'http_request'; + const HTTP_RESPONSE = 'http_response'; + const ENDPOINT = 'endpoint'; + const REQUEST_STATE = 'request_state'; + const LOGOUT = 'logout'; + const EXCEPTION = 'exception'; +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/RequestStateContext.php b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/RequestStateContext.php new file mode 100644 index 0000000000..72b0c39968 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Context/Profile/RequestStateContext.php @@ -0,0 +1,40 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Context\Profile; + +use LightSaml\State\Request\RequestState; + +class RequestStateContext extends AbstractProfileContext +{ + /** @var RequestState */ + protected $requestState; + + /** + * @return RequestState + */ + public function getRequestState() + { + return $this->requestState; + } + + /** + * @param RequestState $requestState + * + * @return RequestStateContext + */ + public function setRequestState($requestState) + { + $this->requestState = $requestState; + + return $this; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/AbstractCredential.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/AbstractCredential.php new file mode 100644 index 0000000000..bfdd8a784c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/AbstractCredential.php @@ -0,0 +1,201 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential; + +use LightSaml\Credential\Context\CredentialContextSet; +use RobRichards\XMLSecLibs\XMLSecurityKey; + +abstract class AbstractCredential implements CredentialInterface +{ + /** @var string */ + private $entityId; + + /** @var string */ + private $usageType; + + /** @var string[] */ + private $keyNames = array(); + + /** @var XMLSecurityKey|null */ + private $publicKey; + + /** @var XMLSecurityKey|null */ + private $privateKey; + + /** @var string|null */ + private $secretKey; + + /** @var CredentialContextSet */ + private $credentialContext; + + public function __construct() + { + $this->credentialContext = new CredentialContextSet(); + } + + /** + * @return string + */ + public function getEntityId() + { + return $this->entityId; + } + + /** + * One of UsageType constants. + * + * @return string|null + */ + public function getUsageType() + { + return $this->usageType; + } + + /** + * @return string[] + */ + public function getKeyNames() + { + return $this->keyNames; + } + + /** + * @return XMLSecurityKey|null + */ + public function getPublicKey() + { + return $this->publicKey; + } + + /** + * @return XMLSecurityKey|null + */ + public function getPrivateKey() + { + return $this->privateKey; + } + + /** + * @return string|null + */ + public function getSecretKey() + { + return $this->secretKey; + } + + /** + * @return CredentialContextSet + */ + public function getCredentialContext() + { + return $this->credentialContext; + } + + /** + * @param CredentialContextSet $credentialContext + * + * @return AbstractCredential + */ + public function setCredentialContext(CredentialContextSet $credentialContext) + { + $this->credentialContext = $credentialContext; + + return $this; + } + + /** + * @param string $entityId + * + * @return AbstractCredential + */ + public function setEntityId($entityId) + { + $this->entityId = $entityId; + + return $this; + } + + /** + * @param \string[] $keyNames + * + * @return AbstractCredential + */ + public function setKeyNames(array $keyNames) + { + $this->keyNames = $keyNames; + + return $this; + } + + /** + * @param string $keyName + * + * @return AbstractCredential + */ + public function addKeyName($keyName) + { + $keyName = trim($keyName); + if ($keyName) { + $this->keyNames[] = $keyName; + } + + return $this; + } + + /** + * @param null|XMLSecurityKey $privateKey + * + * @return AbstractCredential + */ + public function setPrivateKey(XMLSecurityKey $privateKey) + { + $this->privateKey = $privateKey; + + return $this; + } + + /** + * @param null|XMLSecurityKey $publicKey + * + * @return AbstractCredential + */ + public function setPublicKey(XMLSecurityKey $publicKey) + { + $this->publicKey = $publicKey; + + return $this; + } + + /** + * @param null|string $secretKey + * + * @return AbstractCredential + */ + public function setSecretKey($secretKey) + { + $this->secretKey = $secretKey; + + return $this; + } + + /** + * @param string $usageType + * + * @return AbstractCredential + */ + public function setUsageType($usageType) + { + $this->usageType = $usageType; + + return $this; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Context/CredentialContextInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Context/CredentialContextInterface.php new file mode 100644 index 0000000000..56da86743f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Context/CredentialContextInterface.php @@ -0,0 +1,16 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential\Context; + +interface CredentialContextInterface +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Context/CredentialContextSet.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Context/CredentialContextSet.php new file mode 100644 index 0000000000..bbfce4d6a3 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Context/CredentialContextSet.php @@ -0,0 +1,55 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential\Context; + +class CredentialContextSet +{ + /** @var CredentialContextInterface[] */ + protected $contexts = array(); + + /** + * @param CredentialContextInterface[] $contexts + */ + public function __construct(array $contexts = array()) + { + foreach ($contexts as $context) { + if (false == $context instanceof CredentialContextInterface) { + throw new \InvalidArgumentException('Expected CredentialContextInterface'); + } + $this->contexts[] = $context; + } + } + + /** + * @return CredentialContextInterface[] + */ + public function all() + { + return $this->contexts; + } + + /** + * @param string $class + * + * @return CredentialContextInterface|null + */ + public function get($class) + { + foreach ($this->contexts as $context) { + if (get_class($context) == $class || is_subclass_of($context, $class)) { + return $context; + } + } + + return null; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Context/MetadataCredentialContext.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Context/MetadataCredentialContext.php new file mode 100644 index 0000000000..3cdb12e502 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Context/MetadataCredentialContext.php @@ -0,0 +1,64 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential\Context; + +use LightSaml\Model\Metadata\EntityDescriptor; +use LightSaml\Model\Metadata\KeyDescriptor; +use LightSaml\Model\Metadata\RoleDescriptor; + +class MetadataCredentialContext implements CredentialContextInterface +{ + /** @var KeyDescriptor */ + protected $keyDescriptor; + + /** @var RoleDescriptor */ + protected $roleDescriptor; + + /** @var EntityDescriptor */ + protected $entityDescriptor; + + /** + * @param KeyDescriptor $keyDescriptor + * @param RoleDescriptor $roleDescriptor + * @param EntityDescriptor $entityDescriptor + */ + public function __construct(KeyDescriptor $keyDescriptor, RoleDescriptor $roleDescriptor, EntityDescriptor $entityDescriptor) + { + $this->entityDescriptor = $entityDescriptor; + $this->keyDescriptor = $keyDescriptor; + $this->roleDescriptor = $roleDescriptor; + } + + /** + * @return EntityDescriptor + */ + public function getEntityDescriptor() + { + return $this->entityDescriptor; + } + + /** + * @return KeyDescriptor + */ + public function getKeyDescriptor() + { + return $this->keyDescriptor; + } + + /** + * @return RoleDescriptor + */ + public function getRoleDescriptor() + { + return $this->roleDescriptor; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/CredentialInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/CredentialInterface.php new file mode 100644 index 0000000000..2cdf2a4c6b --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/CredentialInterface.php @@ -0,0 +1,55 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential; + +use LightSaml\Credential\Context\CredentialContextSet; +use RobRichards\XMLSecLibs\XMLSecurityKey; + +interface CredentialInterface +{ + /** + * @return string + */ + public function getEntityId(); + + /** + * One of UsageType constants. + * + * @return string|null + */ + public function getUsageType(); + + /** + * @return string[] + */ + public function getKeyNames(); + + /** + * @return XMLSecurityKey|null + */ + public function getPublicKey(); + + /** + * @return XMLSecurityKey|null + */ + public function getPrivateKey(); + + /** + * @return string|null + */ + public function getSecretKey(); + + /** + * @return CredentialContextSet + */ + public function getCredentialContext(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/AlgorithmCriteria.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/AlgorithmCriteria.php new file mode 100644 index 0000000000..35a576b1d9 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/AlgorithmCriteria.php @@ -0,0 +1,34 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential\Criteria; + +class AlgorithmCriteria implements TrustCriteriaInterface +{ + /** @var string */ + protected $algorithm; + + /** + * @param string $algorithm + */ + public function __construct($algorithm) + { + $this->algorithm = $algorithm; + } + + /** + * @return string + */ + public function getAlgorithm() + { + return $this->algorithm; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/CredentialNameCriteria.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/CredentialNameCriteria.php new file mode 100644 index 0000000000..d89b69f02d --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/CredentialNameCriteria.php @@ -0,0 +1,34 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential\Criteria; + +class CredentialNameCriteria implements TrustCriteriaInterface +{ + /** @var string */ + protected $name; + + /** + * @param string $name + */ + public function __construct($name) + { + $this->name = $name; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/EntityIdCriteria.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/EntityIdCriteria.php new file mode 100644 index 0000000000..37f78409c2 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/EntityIdCriteria.php @@ -0,0 +1,34 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential\Criteria; + +class EntityIdCriteria implements TrustCriteriaInterface +{ + /** @var string */ + protected $entityId; + + /** + * @param string $entityId + */ + public function __construct($entityId) + { + $this->entityId = $entityId; + } + + /** + * @return string + */ + public function getEntityId() + { + return $this->entityId; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/MetadataCriteria.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/MetadataCriteria.php new file mode 100644 index 0000000000..f0d411386d --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/MetadataCriteria.php @@ -0,0 +1,52 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential\Criteria; + +use LightSaml\SamlConstants; + +class MetadataCriteria implements TrustCriteriaInterface +{ + const TYPE_IDP = 'idp'; + const TYPE_SP = 'sp'; + + /** @var string */ + protected $metadataType; + + /** @var string */ + protected $protocol; + + /** + * @param string $metadataType + * @param string $protocol + */ + public function __construct($metadataType, $protocol = SamlConstants::PROTOCOL_SAML2) + { + $this->metadataType = $metadataType; + $this->protocol = $protocol; + } + + /** + * @return string + */ + public function getProtocol() + { + return $this->protocol; + } + + /** + * @return string + */ + public function getMetadataType() + { + return $this->metadataType; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/PrivateKeyCriteria.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/PrivateKeyCriteria.php new file mode 100644 index 0000000000..1a48d696a1 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/PrivateKeyCriteria.php @@ -0,0 +1,16 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential\Criteria; + +class PrivateKeyCriteria implements TrustCriteriaInterface +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/PublicKeyThumbprintCriteria.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/PublicKeyThumbprintCriteria.php new file mode 100644 index 0000000000..13dd024cf4 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/PublicKeyThumbprintCriteria.php @@ -0,0 +1,34 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential\Criteria; + +class PublicKeyThumbprintCriteria implements TrustCriteriaInterface +{ + /** @var string */ + private $thumbprint; + + /** + * @param string $thumbprint + */ + public function __construct($thumbprint) + { + $this->thumbprint = $thumbprint; + } + + /** + * @return string + */ + public function getThumbprint() + { + return $this->thumbprint; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/TrustCriteriaInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/TrustCriteriaInterface.php new file mode 100644 index 0000000000..920c36176a --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/TrustCriteriaInterface.php @@ -0,0 +1,18 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential\Criteria; + +use LightSaml\Criteria\CriteriaInterface; + +interface TrustCriteriaInterface extends CriteriaInterface +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/UsageCriteria.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/UsageCriteria.php new file mode 100644 index 0000000000..46d5ac3eca --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/UsageCriteria.php @@ -0,0 +1,34 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential\Criteria; + +class UsageCriteria implements TrustCriteriaInterface +{ + /** @var string */ + protected $usage; + + /** + * @param string $usage + */ + public function __construct($usage) + { + $this->usage = $usage; + } + + /** + * @return string + */ + public function getUsage() + { + return $this->usage; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/X509CredentialCriteria.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/X509CredentialCriteria.php new file mode 100644 index 0000000000..d56b7262b5 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/Criteria/X509CredentialCriteria.php @@ -0,0 +1,16 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential\Criteria; + +class X509CredentialCriteria implements TrustCriteriaInterface +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/KeyHelper.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/KeyHelper.php new file mode 100644 index 0000000000..2941fd5bf6 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/KeyHelper.php @@ -0,0 +1,85 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential; + +use LightSaml\Error\LightSamlSecurityException; +use RobRichards\XMLSecLibs\XMLSecurityKey; + +class KeyHelper +{ + /** + * @param string $key Key content or key filename + * @param string $passphrase Passphrase for the private key + * @param bool $isFile true if $key is a filename of the key + * @param string $type + * + * @return XMLSecurityKey + */ + public static function createPrivateKey($key, $passphrase, $isFile = false, $type = XMLSecurityKey::RSA_SHA1) + { + $result = new XMLSecurityKey($type, array('type' => 'private')); + $result->passphrase = $passphrase; + $result->loadKey($key, $isFile, false); + + return $result; + } + + /** + * @param X509Certificate $certificate + * + * @return XMLSecurityKey + */ + public static function createPublicKey(X509Certificate $certificate) + { + if (null == $certificate->getSignatureAlgorithm()) { + throw new LightSamlSecurityException('Unrecognized certificate signature algorithm'); + } + $key = new XMLSecurityKey($certificate->getSignatureAlgorithm(), array('type' => 'public')); + $key->loadKey($certificate->toPem(), false, true); + + return $key; + } + + /** + * @param XMLSecurityKey $key + * @param string $algorithm + * + * @throws \LightSaml\Error\LightSamlSecurityException + * @throws \InvalidArgumentException + * + * @return XMLSecurityKey + */ + public static function castKey(XMLSecurityKey $key, $algorithm) + { + if (false == is_string($algorithm)) { + throw new \InvalidArgumentException('Algorithm must be string'); + } + + // do nothing if algorithm is already the type of the key + if ($key->type === $algorithm) { + return $key; + } + + $keyInfo = openssl_pkey_get_details($key->key); + if (false === $keyInfo) { + throw new LightSamlSecurityException('Unable to get key details from XMLSecurityKey.'); + } + if (false == isset($keyInfo['key'])) { + throw new LightSamlSecurityException('Missing key in public key details.'); + } + + $newKey = new XMLSecurityKey($algorithm, array('type' => 'public')); + $newKey->loadKey($keyInfo['key']); + + return $newKey; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/UsageType.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/UsageType.php new file mode 100644 index 0000000000..8d710cb449 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/UsageType.php @@ -0,0 +1,21 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential; + +abstract class UsageType +{ + const ENCRYPTION = 'encryption'; + + const SIGNING = 'signing'; + + const UNSPECIFIED = null; +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/X509Certificate.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/X509Certificate.php new file mode 100644 index 0000000000..33d15a252a --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/X509Certificate.php @@ -0,0 +1,270 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential; + +use LightSaml\Error\LightSamlException; +use LightSaml\Error\LightSamlSecurityException; +use LightSaml\SamlConstants; +use RobRichards\XMLSecLibs\XMLSecurityKey; + +class X509Certificate +{ + private static $typeMap = [ + 'RSA-SHA1' => XMLSecurityKey::RSA_SHA1, + 'RSA-SHA256' => XMLSecurityKey::RSA_SHA256, + 'RSA-SHA384' => XMLSecurityKey::RSA_SHA384, + 'RSA-SHA512' => XMLSecurityKey::RSA_SHA512, + ]; + + /** @var string */ + protected $data; + + /** @var null|array */ + protected $info; + + /** @var string */ + private $signatureAlgorithm; + + /** + * @param string $filename + * + * @return X509Certificate + */ + public static function fromFile($filename) + { + $result = new self(); + $result->loadFromFile($filename); + + return $result; + } + + /** + * @param string $data + * + * @return X509Certificate + */ + public function setData($data) + { + $this->data = preg_replace('/\s+/', '', $data); + $this->parse(); + + return $this; + } + + /** + * @return string + */ + public function getData() + { + return $this->data; + } + + /** + * @param string $data + * + * @return X509Certificate + * + * @throws \InvalidArgumentException + */ + public function loadPem($data) + { + $pattern = '/^-----BEGIN CERTIFICATE-----([^-]*)^-----END CERTIFICATE-----/m'; + if (false == preg_match($pattern, $data, $matches)) { + throw new \InvalidArgumentException('Invalid PEM encoded certificate'); + } + $this->data = preg_replace('/\s+/', '', $matches[1]); + $this->parse(); + + return $this; + } + + /** + * @param string $filename + * + * @return X509Certificate + * + * @throws \InvalidArgumentException + */ + public function loadFromFile($filename) + { + if (!is_file($filename)) { + throw new \InvalidArgumentException(sprintf("File not found '%s'", $filename)); + } + $content = file_get_contents($filename); + $this->loadPem($content); + + return $this; + } + + /** + * @return string + */ + public function toPem() + { + $result = "-----BEGIN CERTIFICATE-----\n".chunk_split($this->getData(), 64, "\n")."-----END CERTIFICATE-----\n"; + + return $result; + } + + public function parse() + { + if (false == $this->data) { + throw new LightSamlException('Certificate data not set'); + } + + $res = openssl_x509_read($this->toPem()); + $this->info = openssl_x509_parse($res); + $this->signatureAlgorithm = null; + $signatureType = isset($this->info['signatureTypeSN']) ? $this->info['signatureTypeSN'] : ''; + if ($signatureType && isset(self::$typeMap[$signatureType])) { + $this->signatureAlgorithm = self::$typeMap[$signatureType]; + } else { + openssl_x509_export($res, $out, false); + if (preg_match('/^\s+Signature Algorithm:\s*(.*)\s*$/m', $out, $match)) { + switch ($match[1]) { + case 'sha1WithRSAEncryption': + case 'sha1WithRSA': + $this->signatureAlgorithm = XMLSecurityKey::RSA_SHA1; + break; + case 'sha256WithRSAEncryption': + case 'sha256WithRSA': + $this->signatureAlgorithm = XMLSecurityKey::RSA_SHA256; + break; + case 'sha384WithRSAEncryption': + case 'sha384WithRSA': + $this->signatureAlgorithm = XMLSecurityKey::RSA_SHA384; + break; + case 'sha512WithRSAEncryption': + case 'sha512WithRSA': + $this->signatureAlgorithm = XMLSecurityKey::RSA_SHA512; + break; + case 'md5WithRSAEncryption': + case 'md5WithRSA': + $this->signatureAlgorithm = SamlConstants::XMLDSIG_DIGEST_MD5; + break; + default: + } + } + } + + if (!$this->signatureAlgorithm) { + throw new LightSamlSecurityException('Unrecognized signature algorithm'); + } + } + + /** + * @return string + * + * @throws \LightSaml\Error\LightSamlException + */ + public function getName() + { + if (false == $this->info) { + throw new LightSamlException('Certificate data not set'); + } + + return $this->info['name']; + } + + /** + * @return string + * + * @throws \LightSaml\Error\LightSamlException + */ + public function getSubject() + { + if (false == $this->info) { + throw new LightSamlException('Certificate data not set'); + } + + return $this->info['subject']; + } + + /** + * @return array + * + * @throws \LightSaml\Error\LightSamlException + */ + public function getIssuer() + { + if (false == $this->info) { + throw new LightSamlException('Certificate data not set'); + } + + return $this->info['issuer']; + } + + /** + * @return int + * + * @throws \LightSaml\Error\LightSamlException + */ + public function getValidFromTimestamp() + { + if (false == $this->info) { + throw new LightSamlException('Certificate data not set'); + } + + return $this->info['validFrom_time_t']; + } + + /** + * @return int + * + * @throws \LightSaml\Error\LightSamlException + */ + public function getValidToTimestamp() + { + if (false == $this->info) { + throw new LightSamlException('Certificate data not set'); + } + + return $this->info['validTo_time_t']; + } + + /** + * @return array + * + * @throws \LightSaml\Error\LightSamlException + */ + public function getInfo() + { + if (false == $this->info) { + throw new LightSamlException('Certificate data not set'); + } + + return $this->info; + } + + /** + * @throws \LightSaml\Error\LightSamlException + * + * @return string + */ + public function getFingerprint() + { + if (false == $this->data) { + throw new LightSamlException('Certificate data not set'); + } + + return XMLSecurityKey::getRawThumbprint($this->toPem()); + } + + public function getSignatureAlgorithm() + { + if (false == $this->data) { + throw new LightSamlException('Certificate data not set'); + } + + return $this->signatureAlgorithm; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/X509Credential.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/X509Credential.php new file mode 100644 index 0000000000..4a81cd49db --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/X509Credential.php @@ -0,0 +1,46 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential; + +use RobRichards\XMLSecLibs\XMLSecurityKey; + +class X509Credential extends AbstractCredential implements X509CredentialInterface +{ + /** @var X509Certificate */ + protected $certificate; + + /** + * @param X509Certificate $certificate + * @param XMLSecurityKey $privateKey + */ + public function __construct(X509Certificate $certificate, XMLSecurityKey $privateKey = null) + { + parent::__construct(); + $this->certificate = $certificate; + + $this->setPublicKey(KeyHelper::createPublicKey($certificate)); + + $this->setKeyNames(array($this->getCertificate()->getName())); + + if ($privateKey) { + $this->setPrivateKey($privateKey); + } + } + + /** + * @return X509Certificate + */ + public function getCertificate() + { + return $this->certificate; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Credential/X509CredentialInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/X509CredentialInterface.php new file mode 100644 index 0000000000..f31c0813f9 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Credential/X509CredentialInterface.php @@ -0,0 +1,20 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Credential; + +interface X509CredentialInterface extends CredentialInterface +{ + /** + * @return X509Certificate + */ + public function getCertificate(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Criteria/CriteriaInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Criteria/CriteriaInterface.php new file mode 100644 index 0000000000..4091c8718f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Criteria/CriteriaInterface.php @@ -0,0 +1,16 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Criteria; + +interface CriteriaInterface +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Criteria/CriteriaSet.php b/vendor/lightsaml/lightsaml/src/LightSaml/Criteria/CriteriaSet.php new file mode 100644 index 0000000000..9e3ef0d350 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Criteria/CriteriaSet.php @@ -0,0 +1,143 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Criteria; + +class CriteriaSet +{ + /** @var array|CriteriaInterface[] */ + protected $criterions = array(); + + /** + * @param CriteriaInterface[] $criterions + */ + public function __construct(array $criterions = array()) + { + foreach ($criterions as $criterion) { + $this->add($criterion); + } + } + + /** + * @param CriteriaInterface $criteria + * + * @return CriteriaSet + */ + public function add(CriteriaInterface $criteria) + { + $this->criterions[] = $criteria; + + return $this; + } + + /** + * @param CriteriaInterface $criteria + * + * @return CriteriaSet + */ + public function addIfNone(CriteriaInterface $criteria) + { + if (false == $this->has(get_class($criteria))) { + $this->add($criteria); + } + + return $this; + } + + /** + * @param CriteriaSet $criteriaSet + * + * @return CriteriaSet + */ + public function addAll(CriteriaSet $criteriaSet) + { + foreach ($criteriaSet->all() as $criteria) { + $this->add($criteria); + } + + return $this; + } + + /** + * @param mixed $condition + * @param callable $callback + * + * @return CriteriaSet + */ + public function addIf($condition, $callback) + { + if ($condition) { + $criteria = call_user_func($callback); + if ($criteria) { + $this->add($criteria); + } + } + + return $this; + } + + /** + * @return CriteriaInterface[]|array + */ + public function all() + { + return $this->criterions; + } + + /** + * @param string $class + * + * @return array|CriteriaInterface[] + */ + public function get($class) + { + $result = array(); + foreach ($this->criterions as $criteria) { + if ($criteria instanceof $class) { + $result[] = $criteria; + } + } + + return $result; + } + + /** + * @param string $class + * + * @return CriteriaInterface|null + */ + public function getSingle($class) + { + foreach ($this->criterions as $criteria) { + if ($criteria instanceof $class) { + return $criteria; + } + } + + return null; + } + + /** + * @param string $class + * + * @return bool + */ + public function has($class) + { + foreach ($this->criterions as $criteria) { + if ($criteria instanceof $class) { + return true; + } + } + + return false; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlActionException.php b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlActionException.php new file mode 100644 index 0000000000..1954b0e6df --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlActionException.php @@ -0,0 +1,16 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Error; + +class LightSamlActionException extends LightSamlException +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlAuthenticationException.php b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlAuthenticationException.php new file mode 100644 index 0000000000..e366a92990 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlAuthenticationException.php @@ -0,0 +1,41 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Error; + +use LightSaml\Model\Protocol\StatusResponse; + +class LightSamlAuthenticationException extends LightSamlValidationException +{ + /** @var StatusResponse */ + protected $response; + + /** + * @param StatusResponse $response + * @param string $message + * @param int $code + * @param \Exception $previous + */ + public function __construct(StatusResponse $response, $message = '', $code = 0, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->response = $response; + } + + /** + * @return \LightSaml\Model\Protocol\Response + */ + public function getResponse() + { + return $this->response; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlBindingException.php b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlBindingException.php new file mode 100644 index 0000000000..f016041243 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlBindingException.php @@ -0,0 +1,16 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Error; + +class LightSamlBindingException extends LightSamlException +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlBuildException.php b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlBuildException.php new file mode 100644 index 0000000000..c52bd5b1d8 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlBuildException.php @@ -0,0 +1,16 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Error; + +class LightSamlBuildException extends LightSamlException +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlContextException.php b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlContextException.php new file mode 100644 index 0000000000..40cb0d8f4c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlContextException.php @@ -0,0 +1,41 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Error; + +use LightSaml\Context\ContextInterface; + +class LightSamlContextException extends LightSamlException +{ + /** @var ContextInterface */ + protected $context; + + /** + * @param ContextInterface $context + * @param string $message + * @param int $code + * @param \Exception $previous + */ + public function __construct(ContextInterface $context, $message = '', $code = 0, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->context = $context; + } + + /** + * @return ContextInterface + */ + public function getContext() + { + return $this->context; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlException.php b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlException.php new file mode 100644 index 0000000000..146b3f7038 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlException.php @@ -0,0 +1,16 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Error; + +class LightSamlException extends \RuntimeException +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlModelException.php b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlModelException.php new file mode 100644 index 0000000000..17e40cecbc --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlModelException.php @@ -0,0 +1,16 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Error; + +class LightSamlModelException extends LightSamlException +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlProfileException.php b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlProfileException.php new file mode 100644 index 0000000000..d59050ba38 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlProfileException.php @@ -0,0 +1,16 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Error; + +class LightSamlProfileException extends LightSamlException +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlSecurityException.php b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlSecurityException.php new file mode 100644 index 0000000000..bc85e3848f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlSecurityException.php @@ -0,0 +1,16 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Error; + +class LightSamlSecurityException extends LightSamlException +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlValidationException.php b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlValidationException.php new file mode 100644 index 0000000000..2a80b0caf9 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlValidationException.php @@ -0,0 +1,16 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Error; + +class LightSamlValidationException extends LightSamlException +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlXmlException.php b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlXmlException.php new file mode 100644 index 0000000000..0b311553b3 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Error/LightSamlXmlException.php @@ -0,0 +1,16 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Error; + +class LightSamlXmlException extends LightSamlException +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Event/Events.php b/vendor/lightsaml/lightsaml/src/LightSaml/Event/Events.php new file mode 100644 index 0000000000..b5349b30a8 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Event/Events.php @@ -0,0 +1,19 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Event; + +abstract class Events +{ + const BINDING_MESSAGE_RECEIVED = 'lightsaml.binding_message_received'; + const BINDING_MESSAGE_SENT = 'lightsaml.binding_message_sent'; + const BEFORE_ENCRYPT = 'lightsaml.before_encrypt'; +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Helper.php b/vendor/lightsaml/lightsaml/src/LightSaml/Helper.php new file mode 100644 index 0000000000..f456612344 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Helper.php @@ -0,0 +1,216 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml; + +final class Helper +{ + const TIME_FORMAT = 'Y-m-d\TH:i:s\Z'; + + /** + * @param string $duration + */ + public static function validateDurationString($duration) + { + if ($duration) { + try { + new \DateInterval((string) $duration); + } catch (\Exception $ex) { + throw new \InvalidArgumentException(sprintf("Invalid duration '%s' format", $duration), 0, $ex); + } + } + } + + /** + * @param int $time + * + * @return string + */ + public static function time2string($time) + { + return gmdate('Y-m-d\TH:i:s\Z', $time); + } + + /** + * @param int|string|\DateTime $value + * + * @return int + * + * @throws \InvalidArgumentException + */ + public static function getTimestampFromValue($value) + { + if (is_string($value)) { + return self::parseSAMLTime($value); + } elseif ($value instanceof \DateTime) { + return $value->getTimestamp(); + } elseif (is_int($value)) { + return $value; + } else { + throw new \InvalidArgumentException(); + } + } + + /** + * @param string $time + * + * @return int + * + * @throws \InvalidArgumentException + */ + public static function parseSAMLTime($time) + { + $matches = []; + if (0 == preg_match( + '/^(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)T(\\d\\d):(\\d\\d):(\\d\\d)(?:\\.\\d+)?(Z|[+-]\\d\\d:\\d\\d)$/D', + $time, + $matches + )) { + throw new \InvalidArgumentException('Invalid SAML2 timestamp: '.$time); + } + + return strtotime($time); + } + + /** + * @param int $length + * + * @return string + * + * @throws \InvalidArgumentException + */ + public static function generateRandomBytes($length) + { + $length = intval($length); + if ($length <= 0) { + throw new \InvalidArgumentException(); + } + + if (function_exists('openssl_random_pseudo_bytes')) { + return openssl_random_pseudo_bytes($length); + } + + $data = ''; + for ($i = 0; $i < $length; ++$i) { + $data .= chr(mt_rand(0, 255)); + } + + return $data; + } + + /** + * @param string $bytes + * + * @return string + */ + public static function stringToHex($bytes) + { + $result = ''; + $len = strlen($bytes); + for ($i = 0; $i < $len; ++$i) { + $result .= sprintf('%02x', ord($bytes[$i])); + } + + return $result; + } + + /** + * @return string + */ + public static function generateID() + { + return '_'.self::stringToHex(self::generateRandomBytes(21)); + } + + /** + * Is ID element at least 128 bits in length (SAML2.0 standard section 1.3.4). + * + * @param string $id + * + * @return bool + */ + public static function validateIdString($id) + { + return is_string($id) && strlen(trim($id)) >= 16; + } + + /** + * @param string $value + * + * @return bool + */ + public static function validateRequiredString($value) + { + return is_string($value) && strlen(trim($value)) > 0; + } + + /** + * @param string $value + * + * @return bool + */ + public static function validateOptionalString($value) + { + return null === $value || self::validateRequiredString($value); + } + + /** + * @param string $value + * + * @return bool + */ + public static function validateWellFormedUriString($value) + { + $value = trim($value); + if ('' == $value || strlen($value) > 65520) { + return false; + } + + if (preg_match('|\s|', $value)) { + return false; + } + + $parts = parse_url($value); + if (isset($parts['scheme'])) { + if ($parts['scheme'] != rawurlencode($parts['scheme'])) { + return false; + } + } else { + return false; + } + + return true; + } + + /** + * @param int $notBefore + * @param int $now + * @param int $allowedSecondsSkew + * + * @return bool + */ + public static function validateNotBefore($notBefore, $now, $allowedSecondsSkew) + { + return null == $notBefore || (($notBefore - $allowedSecondsSkew) < $now); + } + + /** + * @param int $notOnOrAfter + * @param int $now + * @param int $allowedSecondsSkew + * + * @return bool + */ + public static function validateNotOnOrAfter($notOnOrAfter, $now, $allowedSecondsSkew) + { + return null == $notOnOrAfter || ($now < ($notOnOrAfter + $allowedSecondsSkew)); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Meta/ParameterBag.php b/vendor/lightsaml/lightsaml/src/LightSaml/Meta/ParameterBag.php new file mode 100644 index 0000000000..fc55e44283 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Meta/ParameterBag.php @@ -0,0 +1,142 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Meta; + +class ParameterBag implements \IteratorAggregate, \Countable, \Serializable +{ + /** + * Parameter storage. + * + * @var array + */ + protected $parameters; + + /** + * @param array $parameters An array of parameters + */ + public function __construct(array $parameters = array()) + { + $this->parameters = $parameters; + } + + /** + * Returns the parameters. + * + * @return array An array of parameters + */ + public function all() + { + return $this->parameters; + } + + /** + * Returns the parameter keys. + * + * @return array An array of parameter keys + */ + public function keys() + { + return array_keys($this->parameters); + } + + /** + * Replaces the current parameters by a new set. + * + * @param array $parameters An array of parameters + */ + public function replace(array $parameters = array()) + { + $this->parameters = $parameters; + } + + /** + * Adds parameters. + * + * @param array $parameters + */ + public function add(array $parameters = array()) + { + $this->parameters = array_replace($this->parameters, $parameters); + } + + /** + * Returns a parameter by name. + * + * @param string $key + * @param mixed $default + * + * @return mixed + */ + public function get($key, $default = null) + { + return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default; + } + + /** + * Sets a parameter by name. + * + * @param string $key + * @param mixed $value + */ + public function set($key, $value) + { + $this->parameters[$key] = $value; + } + + /** + * Returns true if the parameter is defined. + * + * @param string $key + * + * @return bool true if the parameter exists, false otherwise + */ + public function has($key) + { + return array_key_exists($key, $this->parameters); + } + + /** + * Removes a parameter. + * + * @param string $key + */ + public function remove($key) + { + unset($this->parameters[$key]); + } + + /** + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->parameters); + } + + /** + * @return int + */ + public function count() + { + return count($this->parameters); + } + + public function serialize() + { + return serialize($this->parameters); + } + + public function unserialize($serialized) + { + $this->parameters = unserialize($serialized); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Meta/SigningOptions.php b/vendor/lightsaml/lightsaml/src/LightSaml/Meta/SigningOptions.php new file mode 100644 index 0000000000..ad579555d4 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Meta/SigningOptions.php @@ -0,0 +1,113 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Meta; + +use LightSaml\Credential\X509Certificate; +use RobRichards\XMLSecLibs\XMLSecurityKey; + +class SigningOptions +{ + const CERTIFICATE_SUBJECT_NAME = 'subjectName'; + const CERTIFICATE_ISSUER_SERIAL = 'issuerSerial'; + + /** @var bool */ + private $enabled = true; + + /** @var XMLSecurityKey */ + private $privateKey; + + /** @var X509Certificate */ + private $certificate; + + /** @var ParameterBag */ + private $certificateOptions; + + /** + * @param XMLSecurityKey $privateKey + * @param X509Certificate $certificate + */ + public function __construct(XMLSecurityKey $privateKey = null, X509Certificate $certificate = null) + { + $this->enabled = true; + $this->privateKey = $privateKey; + $this->certificate = $certificate; + $this->certificateOptions = new ParameterBag(); + } + + /** + * @return X509Certificate + */ + public function getCertificate() + { + return $this->certificate; + } + + /** + * @param X509Certificate $certificate + * + * @return SigningOptions + */ + public function setCertificate(X509Certificate $certificate = null) + { + $this->certificate = $certificate; + + return $this; + } + + /** + * @return XMLSecurityKey + */ + public function getPrivateKey() + { + return $this->privateKey; + } + + /** + * @param XMLSecurityKey $privateKey + * + * @return SigningOptions + */ + public function setPrivateKey(XMLSecurityKey $privateKey = null) + { + $this->privateKey = $privateKey; + + return $this; + } + + /** + * @return ParameterBag + */ + public function getCertificateOptions() + { + return $this->certificateOptions; + } + + /** + * @return bool + */ + public function isEnabled() + { + return $this->enabled; + } + + /** + * @param bool $enabled + * + * @return SigningOptions + */ + public function setEnabled($enabled) + { + $this->enabled = (bool) $enabled; + + return $this; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Meta/TrustOptions/TrustOptions.php b/vendor/lightsaml/lightsaml/src/LightSaml/Meta/TrustOptions/TrustOptions.php new file mode 100644 index 0000000000..0b9eee51af --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Meta/TrustOptions/TrustOptions.php @@ -0,0 +1,202 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Meta\TrustOptions; + +use RobRichards\XMLSecLibs\XMLSecurityDSig; +use RobRichards\XMLSecLibs\XMLSecurityKey; + +class TrustOptions +{ + /** @var bool */ + protected $signAuthnRequest = false; + + /** @var bool */ + protected $encryptAuthnRequest = false; + + /** @var bool */ + protected $signAssertions = true; + + /** @var bool */ + protected $encryptAssertions = true; + + /** @var bool */ + protected $signResponse = true; + + /** @var string */ + protected $signatureDigestAlgorithm = XMLSecurityDSig::SHA1; + + /** @var string */ + protected $blockEncryptionAlgorithm = XMLSecurityKey::AES128_CBC; + + /** @var string */ + protected $keyTransportEncryptionAlgorithm = XMLSecurityKey::RSA_OAEP_MGF1P; + + /** + * @return bool + */ + public function getEncryptAssertions() + { + return $this->encryptAssertions; + } + + /** + * @param bool $encryptAssertions + * + * @return TrustOptions + */ + public function setEncryptAssertions($encryptAssertions) + { + $this->encryptAssertions = (bool) $encryptAssertions; + + return $this; + } + + /** + * @return bool + */ + public function getEncryptAuthnRequest() + { + return $this->encryptAuthnRequest; + } + + /** + * @param bool $encryptAuthnRequest + * + * @return TrustOptions + */ + public function setEncryptAuthnRequest($encryptAuthnRequest) + { + $this->encryptAuthnRequest = (bool) $encryptAuthnRequest; + + return $this; + } + + /** + * @return bool + */ + public function getSignAssertions() + { + return $this->signAssertions; + } + + /** + * @param bool $signAssertions + * + * @return TrustOptions + */ + public function setSignAssertions($signAssertions) + { + $this->signAssertions = (bool) $signAssertions; + + return $this; + } + + /** + * @return bool + */ + public function getSignAuthnRequest() + { + return $this->signAuthnRequest; + } + + /** + * @param bool $signAuthnRequest + * + * @return TrustOptions + */ + public function setSignAuthnRequest($signAuthnRequest) + { + $this->signAuthnRequest = (bool) $signAuthnRequest; + + return $this; + } + + /** + * @return bool + */ + public function getSignResponse() + { + return $this->signResponse; + } + + /** + * @param bool $signResponse + * + * @return TrustOptions + */ + public function setSignResponse($signResponse) + { + $this->signResponse = (bool) $signResponse; + + return $this; + } + + /** + * @return string + */ + public function getSignatureDigestAlgorithm() + { + return $this->signatureDigestAlgorithm; + } + + /** + * @param string $signatureDigestAlgorithm + * + * @return TrustOptions + */ + public function setSignatureDigestAlgorithm($signatureDigestAlgorithm) + { + $this->signatureDigestAlgorithm = $signatureDigestAlgorithm; + + return $this; + } + + /** + * @return string + */ + public function getBlockEncryptionAlgorithm() + { + return $this->blockEncryptionAlgorithm; + } + + /** + * @param string $blockEncryptionAlgorithm + * + * @return TrustOptions + */ + public function setBlockEncryptionAlgorithm($blockEncryptionAlgorithm) + { + $this->blockEncryptionAlgorithm = $blockEncryptionAlgorithm; + + return $this; + } + + /** + * @return string + */ + public function getKeyTransportEncryptionAlgorithm() + { + return $this->keyTransportEncryptionAlgorithm; + } + + /** + * @param string $keyTransportEncryptionAlgorithm + * + * @return TrustOptions + */ + public function setKeyTransportEncryptionAlgorithm($keyTransportEncryptionAlgorithm) + { + $this->keyTransportEncryptionAlgorithm = $keyTransportEncryptionAlgorithm; + + return $this; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/AbstractSamlModel.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/AbstractSamlModel.php new file mode 100644 index 0000000000..a28eaa71ad --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/AbstractSamlModel.php @@ -0,0 +1,333 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model; + +use LightSaml\Error\LightSamlXmlException; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; + +abstract class AbstractSamlModel implements SamlElementInterface +{ + /** + * @param string $name + * @param null|string $namespace + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return \DOMElement + */ + protected function createElement($name, $namespace, \DOMNode $parent, SerializationContext $context) + { + if ($namespace) { + $result = $context->getDocument()->createElementNS($namespace, $name); + } else { + $result = $context->getDocument()->createElement($name); + } + $parent->appendChild($result); + + return $result; + } + + /** + * @param string $name + * @param \DOMNode $parent + * @param SerializationContext $context + * @param string|null $namespace + * + * @throws \LogicException + */ + private function oneElementToXml($name, \DOMNode $parent, SerializationContext $context, $namespace = null) + { + $value = $this->getPropertyValue($name); + if (null == $value) { + return; + } + if ($value instanceof SamlElementInterface) { + $value->serialize($parent, $context); + } elseif (is_string($value)) { + if ($namespace) { + $node = $context->getDocument()->createElementNS($namespace, $name, $value); + } else { + $node = $context->getDocument()->createElement($name, $value); + } + $parent->appendChild($node); + } else { + throw new \LogicException(sprintf("Element '%s' must implement SamlElementInterface or be a string", $name)); + } + } + + /** + * @param array|string[] $names + * @param \DOMNode $parent + * @param SerializationContext $context + * @param string|null $namespace + */ + protected function singleElementsToXml(array $names, \DOMNode $parent, SerializationContext $context, $namespace = null) + { + foreach ($names as $name) { + $this->oneElementToXml($name, $parent, $context, $namespace); + } + } + + /** + * @param array|null $value + * @param \DOMNode $node + * @param SerializationContext $context + * @param null|string $nodeName + * @param null|string $namespaceUri + * + * @throws \LogicException + */ + protected function manyElementsToXml($value, \DOMNode $node, SerializationContext $context, $nodeName = null, $namespaceUri = null) + { + if (false == $value) { + return; + } + + if (false == is_array($value)) { + throw new \LogicException('value must be array or null'); + } + + foreach ($value as $object) { + if ($object instanceof SamlElementInterface) { + if ($nodeName) { + throw new \LogicException('nodeName should not be specified when serializing array of SamlElementInterface'); + } + $object->serialize($node, $context); + } elseif ($nodeName) { + if ($namespaceUri) { + $child = $context->getDocument()->createElementNS($namespaceUri, $nodeName, (string) $object); + } else { + $child = $context->getDocument()->createElement($nodeName, (string) $object); + } + $node->appendChild($child); + } else { + throw new \LogicException('Can handle only array of AbstractSamlModel or strings with nodeName parameter specified'); + } + } + } + + /** + * @param \DOMElement $node + * @param DeserializationContext $context + * @param string $nodeName + * @param string|null $namespacePrefix + * @param string $class + * @param string $methodName + * + * @throws \LogicException + */ + protected function manyElementsFromXml(\DOMElement $node, DeserializationContext $context, $nodeName, $namespacePrefix, $class, $methodName) + { + if ($namespacePrefix) { + $query = sprintf('%s:%s', $namespacePrefix, $nodeName); + } else { + $query = sprintf('%s', $nodeName); + } + + foreach ($context->getXpath()->query($query, $node) as $xml) { + /* @var \DOMElement $xml */ + if ($class) { + /** @var SamlElementInterface $object */ + $object = new $class(); + if (false == $object instanceof SamlElementInterface) { + throw new \LogicException(sprintf("Node '%s' class '%s' must implement SamlElementInterface", $nodeName, $class)); + } + $object->deserialize($xml, $context); + $this->{$methodName}($object); + } else { + $object = $xml->textContent; + $this->{$methodName}($object); + } + } + } + + /** + * @param string $name + * @param \DOMElement $element + * + * @throws \LogicException + * + * @return bool True if property value is not empty and attribute was set to the element + */ + protected function singleAttributeToXml($name, \DOMElement $element) + { + $value = $this->getPropertyValue($name); + if (null !== $value && '' !== $value) { + if (is_bool($value)) { + $element->setAttribute($name, $value ? 'true' : 'false'); + } else { + $element->setAttribute($name, $value); + } + + return true; + } + + return false; + } + + /** + * @param array|string[] $names + * @param \DOMElement $element + */ + protected function attributesToXml(array $names, \DOMElement $element) + { + foreach ($names as $name) { + $this->singleAttributeToXml($name, $element); + } + } + + /** + * @param \DOMNode $node + * @param string $expectedName + * @param string $expectedNamespaceUri + */ + protected function checkXmlNodeName(\DOMNode &$node, $expectedName, $expectedNamespaceUri) + { + if ($node instanceof \DOMDocument) { + $node = $node->firstChild; + } + while ($node && $node instanceof \DOMComment) { + $node = $node->nextSibling; + } + if (null === $node) { + throw new LightSamlXmlException(sprintf( + "Unable to find expected '%s' xml node and '%s' namespace", + $expectedName, + $expectedNamespaceUri + )); + } elseif ($node->localName != $expectedName || $node->namespaceURI != $expectedNamespaceUri) { + throw new LightSamlXmlException(sprintf( + "Expected '%s' xml node and '%s' namespace but got node '%s' and namespace '%s'", + $expectedName, + $expectedNamespaceUri, + $node->localName, + $node->namespaceURI + )); + } + } + + /** + * @param \DOMElement $node + * @param string $attributeName + */ + protected function singleAttributeFromXml(\DOMElement $node, $attributeName) + { + $value = $node->getAttribute($attributeName); + if ('' !== $value) { + $setter = 'set'.$attributeName; + if (method_exists($this, $setter)) { + $this->{$setter}($value); + } + } + } + + /** + * @param \DOMElement $node + * @param DeserializationContext $context + * @param string $elementName + * @param string $class + * @param string $namespacePrefix + * + * @throws \LogicException + */ + protected function oneElementFromXml(\DOMElement $node, DeserializationContext $context, $elementName, $class, $namespacePrefix) + { + if ($namespacePrefix) { + $query = sprintf('./%s:%s', $namespacePrefix, $elementName); + } else { + $query = sprintf('./%s', $elementName); + } + $arr = $context->getXpath()->query($query, $node); + $value = $arr->length > 0 ? $arr->item(0) : null; + + if ($value) { + $setter = 'set'.$elementName; + if (false == method_exists($this, $setter)) { + throw new \LogicException(sprintf( + "Unable to find setter for element '%s' in class '%s'", + $elementName, + get_class($this) + )); + } + + if ($class) { + /** @var AbstractSamlModel $object */ + $object = new $class(); + if (false == $object instanceof \LightSaml\Model\SamlElementInterface) { + throw new \LogicException(sprintf( + "Specified class '%s' for element '%s' must implement SamlElementInterface", + $class, + $elementName + )); + } + + $object->deserialize($value, $context); + } else { + $object = $value->textContent; + } + + $this->{$setter}($object); + } + } + + /** + * @param \DOMElement $node + * @param DeserializationContext $context + * @param array $options elementName=>class + */ + protected function singleElementsFromXml(\DOMElement $node, DeserializationContext $context, array $options) + { + foreach ($options as $elementName => $info) { + $this->oneElementFromXml($node, $context, $elementName, $info[1], $info[0]); + } + } + + /** + * @param \DOMElement $node + * @param array $attributeNames + */ + protected function attributesFromXml(\DOMElement $node, array $attributeNames) + { + foreach ($attributeNames as $attributeName) { + $this->singleAttributeFromXml($node, $attributeName); + } + } + + /** + * @param string $name + * + * @return mixed + * + * @throws \LogicException + */ + private function getPropertyValue($name) + { + if (false !== ($pos = strpos($name, ':'))) { + $name = substr($name, $pos + 1); + } + $getter = 'get'.$name.'String'; + if (false == method_exists($this, $getter)) { + $getter = 'get'.$name; + } + if (false == method_exists($this, $getter)) { + throw new \LogicException(sprintf( + "Unable to find getter method for '%s' on '%s'", + $name, + get_class($this) + )); + } + $value = $this->{$getter}(); + + return $value; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AbstractCondition.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AbstractCondition.php new file mode 100644 index 0000000000..5a9458b614 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AbstractCondition.php @@ -0,0 +1,18 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Model\AbstractSamlModel; + +abstract class AbstractCondition extends AbstractSamlModel +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AbstractNameID.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AbstractNameID.php new file mode 100644 index 0000000000..eef13c1f31 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AbstractNameID.php @@ -0,0 +1,200 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Error\LightSamlModelException; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\SamlConstants; + +abstract class AbstractNameID extends AbstractSamlModel +{ + /** + * @var string + */ + protected $value; + + /** + * @var string|null + */ + protected $format; + + /** + * @var string|null + */ + protected $nameQualifier; + + /** + * @var string|null + */ + protected $spNameQualifier; + + /** + * @var string|null + */ + protected $spProvidedId; + + /** + * @param string $value + * @param string $format + */ + public function __construct($value = null, $format = null) + { + $this->value = $value; + $this->format = $format; + } + + /** + * @param null|string $format + * + * @return AbstractNameID + */ + public function setFormat($format) + { + $this->format = (string) $format; + + return $this; + } + + /** + * @return null|string + */ + public function getFormat() + { + return $this->format; + } + + /** + * @param null|string $nameQualifier + * + * @return AbstractNameID + */ + public function setNameQualifier($nameQualifier) + { + $this->nameQualifier = (string) $nameQualifier; + + return $this; + } + + /** + * @return null|string + */ + public function getNameQualifier() + { + return $this->nameQualifier; + } + + /** + * @param null|string $spNameQualifier + * + * @return AbstractNameID + */ + public function setSPNameQualifier($spNameQualifier) + { + $this->spNameQualifier = (string) $spNameQualifier; + + return $this; + } + + /** + * @return null|string + */ + public function getSPNameQualifier() + { + return $this->spNameQualifier; + } + + /** + * @param null|string $spProvidedId + * + * @return AbstractNameID + */ + public function setSPProvidedID($spProvidedId) + { + $this->spProvidedId = (string) $spProvidedId; + + return $this; + } + + /** + * @return null|string + */ + public function getSPProvidedID() + { + return $this->spProvidedId; + } + + /** + * @param string $value + * + * @return AbstractNameID + */ + public function setValue($value) + { + $this->value = (string) $value; + + return $this; + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } + + protected function prepareForXml() + { + if (false == $this->getValue()) { + throw new LightSamlModelException('NameID value not set'); + } + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return \DOMElement + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $this->prepareForXml(); + if (SamlConstants::NS_ASSERTION == $parent->namespaceURI) { + $result = $this->createElement($this->getElementName(), SamlConstants::NS_ASSERTION, $parent, $context); + } else { + $result = $this->createElement('saml:'.$this->getElementName(), SamlConstants::NS_ASSERTION, $parent, $context); + } + + /* @var \DOMElement $parent */ + $this->attributesToXml(array('Format', 'NameQualifier', 'SPNameQualifier', 'SPProvidedID'), $result); + $result->nodeValue = $this->getValue(); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, $this->getElementName(), SamlConstants::NS_ASSERTION); + + $this->attributesFromXml($node, array('NameQualifier', 'Format', 'SPNameQualifier', 'SPProvidedID')); + $this->setValue($node->textContent); + } + + /** + * @return string + */ + abstract protected function getElementName(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AbstractStatement.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AbstractStatement.php new file mode 100644 index 0000000000..d991686507 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AbstractStatement.php @@ -0,0 +1,18 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Model\AbstractSamlModel; + +abstract class AbstractStatement extends AbstractSamlModel +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Advice.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Advice.php new file mode 100644 index 0000000000..54c8392dce --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Advice.php @@ -0,0 +1,16 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +class Advice +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Assertion.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Assertion.php new file mode 100644 index 0000000000..804988b6b8 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Assertion.php @@ -0,0 +1,473 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Helper; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\Model\XmlDSig\Signature; +use LightSaml\SamlConstants; + +class Assertion extends AbstractSamlModel +{ + //region Attributes + + /** + * @var string + */ + protected $id; + + /** + * @var string + */ + protected $version = SamlConstants::VERSION_20; + + /** + * @var int + */ + protected $issueInstant; + + //endregion + + //region Elements + + /** + * @var Issuer + */ + protected $issuer; + + /** + * @var Signature|null + */ + protected $signature; + + /** + * @var Subject|null + */ + protected $subject; + + /** + * @var Conditions|null + */ + protected $conditions; + + /** + * @var array|AbstractStatement[]|AuthnStatement[]|AttributeStatement[] + */ + protected $items = array(); + + //endregion + + /** + * Core 3.3.4 Processing rules. + * + * @param string $nameId + * @param string|null $format + * + * @return bool + */ + public function equals($nameId, $format) + { + if (false == $this->getSubject()) { + return false; + } + + if (false == $this->getSubject()->getNameID()) { + return false; + } + + if ($this->getSubject()->getNameID()->getValue() != $nameId) { + return false; + } + + if ($this->getSubject()->getNameID()->getFormat() != $format) { + return false; + } + + return true; + } + + /** + * @param string $sessionIndex + * + * @return bool + */ + public function hasSessionIndex($sessionIndex) + { + if (null == $this->getAllAuthnStatements()) { + return false; + } + + foreach ($this->getAllAuthnStatements() as $authnStatement) { + if ($authnStatement->getSessionIndex() == $sessionIndex) { + return true; + } + } + + return false; + } + + public function hasAnySessionIndex() + { + if (false == $this->getAllAuthnStatements()) { + return false; + } + + foreach ($this->getAllAuthnStatements() as $authnStatement) { + if ($authnStatement->getSessionIndex()) { + return true; + } + } + + return false; + } + + //region Getters & Setters + + /** + * @param Conditions|null $conditions + * + * @return Assertion + */ + public function setConditions(Conditions $conditions = null) + { + $this->conditions = $conditions; + + return $this; + } + + /** + * @return \LightSaml\Model\Assertion\Conditions|null + */ + public function getConditions() + { + return $this->conditions; + } + + /** + * @param string $id + * + * @return Assertion + */ + public function setId($id) + { + $this->id = (string) $id; + + return $this; + } + + /** + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * @param string|int|\DateTime $issueInstant + * + * @throws \InvalidArgumentException + * + * @return Assertion + */ + public function setIssueInstant($issueInstant) + { + $this->issueInstant = Helper::getTimestampFromValue($issueInstant); + + return $this; + } + + /** + * @return int + */ + public function getIssueInstantTimestamp() + { + return $this->issueInstant; + } + + /** + * @return string + */ + public function getIssueInstantString() + { + if ($this->issueInstant) { + return Helper::time2string($this->issueInstant); + } + + return null; + } + + /** + * @return string + */ + public function getIssueInstantDateTime() + { + if ($this->issueInstant) { + return new \DateTime('@'.$this->issueInstant); + } + + return null; + } + + /** + * @param Issuer $issuer + * + * @return Assertion + */ + public function setIssuer(Issuer $issuer = null) + { + $this->issuer = $issuer; + + return $this; + } + + /** + * @return \LightSaml\Model\Assertion\Issuer + */ + public function getIssuer() + { + return $this->issuer; + } + + /** + * @param Signature $signature + * + * @return Assertion + */ + public function setSignature(Signature $signature = null) + { + $this->signature = $signature; + + return $this; + } + + /** + * @return \LightSaml\Model\XmlDSig\Signature|null + */ + public function getSignature() + { + return $this->signature; + } + + /** + * @param Subject $subject + * + * @return Assertion + */ + public function setSubject(Subject $subject) + { + $this->subject = $subject; + + return $this; + } + + /** + * @return \LightSaml\Model\Assertion\Subject + */ + public function getSubject() + { + return $this->subject; + } + + /** + * @param string $version + * + * @return Assertion + */ + public function setVersion($version) + { + $this->version = (string) $version; + + return $this; + } + + /** + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * @param AbstractStatement $statement + * + * @return Assertion + */ + public function addItem(AbstractStatement $statement) + { + $this->items[] = $statement; + + return $this; + } + + /** + * @return AbstractStatement[]|AttributeStatement[]|AuthnStatement[]|array + */ + public function getAllItems() + { + return $this->items; + } + + /** + * @return \LightSaml\Model\Assertion\AuthnStatement[] + */ + public function getAllAuthnStatements() + { + $result = array(); + foreach ($this->items as $item) { + if ($item instanceof AuthnStatement) { + $result[] = $item; + } + } + + return $result; + } + + /** + * @return \LightSaml\Model\Assertion\AttributeStatement[] + */ + public function getAllAttributeStatements() + { + $result = array(); + foreach ($this->items as $item) { + if ($item instanceof AttributeStatement) { + $result[] = $item; + } + } + + return $result; + } + + /** + * @return \LightSaml\Model\Assertion\AttributeStatement|null + */ + public function getFirstAttributeStatement() + { + foreach ($this->items as $item) { + if ($item instanceof AttributeStatement) { + return $item; + } + } + + return null; + } + + /** + * @return \LightSaml\Model\Assertion\AuthnStatement|null + */ + public function getFirstAuthnStatement() + { + foreach ($this->items as $item) { + if ($item instanceof AuthnStatement) { + return $item; + } + } + + return null; + } + + //endregion + + /** + * @return bool + */ + public function hasBearerSubject() + { + if ($this->getAllAuthnStatements() && $this->getSubject()) { + if ($this->getSubject()->getBearerConfirmations()) { + return true; + } + } + + return false; + } + + protected function prepareForXml() + { + if (false == $this->getId()) { + $this->setId(Helper::generateID()); + } + if (false == $this->getIssueInstantTimestamp()) { + $this->setIssueInstant(time()); + } + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $this->prepareForXml(); + + $result = $this->createElement('Assertion', SamlConstants::NS_ASSERTION, $parent, $context); + + $this->attributesToXml(array('ID', 'Version', 'IssueInstant'), $result); + + $this->singleElementsToXml( + array('Issuer', 'Subject', 'Conditions'), + $result, + $context + ); + + foreach ($this->items as $item) { + $item->serialize($result, $context); + } + + // must be added at the end + $this->singleElementsToXml(array('Signature'), $result, $context); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'Assertion', SamlConstants::NS_ASSERTION); + + $this->attributesFromXml($node, array('ID', 'Version', 'IssueInstant')); + + $this->singleElementsFromXml($node, $context, array( + 'Issuer' => array('saml', 'LightSaml\Model\Assertion\Issuer'), + 'Subject' => array('saml', 'LightSaml\Model\Assertion\Subject'), + 'Conditions' => array('saml', 'LightSaml\Model\Assertion\Conditions'), + )); + + $this->manyElementsFromXml( + $node, + $context, + 'AuthnStatement', + 'saml', + 'LightSaml\Model\Assertion\AuthnStatement', + 'addItem' + ); + + $this->manyElementsFromXml( + $node, + $context, + 'AttributeStatement', + 'saml', + 'LightSaml\Model\Assertion\AttributeStatement', + 'addItem' + ); + + $this->singleElementsFromXml($node, $context, array( + 'Signature' => array('ds', 'LightSaml\Model\XmlDSig\SignatureXmlReader'), + )); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Attribute.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Attribute.php new file mode 100644 index 0000000000..4b193cc8ca --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Attribute.php @@ -0,0 +1,181 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\SamlConstants; + +class Attribute extends AbstractSamlModel +{ + /** @var string */ + protected $name; + + /** @var string */ + protected $nameFormat; + + /** @var string */ + protected $friendlyName; + + /** @var string[] */ + protected $attributeValue; + + /** + * @param string|null $name + * @param string|string[] $value + */ + public function __construct($name = null, $value = null) + { + $this->name = $name; + if ($value) { + $this->attributeValue = is_array($value) ? $value : array($value); + } + } + + /** + * @param string $attributeValue + * + * @return Attribute + */ + public function addAttributeValue($attributeValue) + { + if (false == is_array($this->attributeValue)) { + $this->attributeValue = array(); + } + $this->attributeValue[] = $attributeValue; + + return $this; + } + + /** + * @param string[]|string $attributeValue + * + * @return Attribute + */ + public function setAttributeValue($attributeValue) + { + if (false == is_array($attributeValue)) { + $attributeValue = array($attributeValue); + } + $this->attributeValue = $attributeValue; + + return $this; + } + + /** + * @return \string[] + */ + public function getAllAttributeValues() + { + return $this->attributeValue; + } + + /** + * @return string|null + */ + public function getFirstAttributeValue() + { + $arr = $this->attributeValue; + + return array_shift($arr); + } + + /** + * @param string $friendlyName + * + * @return Attribute + */ + public function setFriendlyName($friendlyName) + { + $this->friendlyName = $friendlyName; + + return $this; + } + + /** + * @return string + */ + public function getFriendlyName() + { + return $this->friendlyName; + } + + /** + * @param string $name + * + * @return Attribute + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $nameFormat + * + * @return Attribute + */ + public function setNameFormat($nameFormat) + { + $this->nameFormat = $nameFormat; + + return $this; + } + + /** + * @return string + */ + public function getNameFormat() + { + return $this->nameFormat; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('Attribute', SamlConstants::NS_ASSERTION, $parent, $context); + + $this->attributesToXml(array('Name', 'NameFormat', 'FriendlyName'), $result); + + $this->manyElementsToXml($this->getAllAttributeValues(), $result, $context, 'AttributeValue', SamlConstants::NS_ASSERTION); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'Attribute', SamlConstants::NS_ASSERTION); + + $this->attributesFromXml($node, array('Name', 'NameFormat', 'FriendlyName')); + + $this->attributeValue = array(); + $this->manyElementsFromXml($node, $context, 'AttributeValue', 'saml', null, 'addAttributeValue'); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AttributeStatement.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AttributeStatement.php new file mode 100644 index 0000000000..abbe090c4e --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AttributeStatement.php @@ -0,0 +1,94 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\SamlConstants; + +class AttributeStatement extends AbstractStatement +{ + /** + * @var Attribute[] + */ + protected $attributes = array(); + + /** + * @param Attribute $attribute + * + * @return AttributeStatement + */ + public function addAttribute(Attribute $attribute) + { + $this->attributes[] = $attribute; + + return $this; + } + + /** + * @return \LightSaml\Model\Assertion\Attribute[] + */ + public function getAllAttributes() + { + return $this->attributes; + } + + /** + * @param string $name + * + * @return Attribute|null + */ + public function getFirstAttributeByName($name) + { + if (is_array($this->getAllAttributes())) { + foreach ($this->getAllAttributes() as $attribute) { + if (null == $name || $attribute->getName() == $name) { + return $attribute; + } + } + } + + return null; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('AttributeStatement', SamlConstants::NS_ASSERTION, $parent, $context); + + $this->manyElementsToXml($this->getAllAttributes(), $result, $context, null); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'AttributeStatement', SamlConstants::NS_ASSERTION); + + $this->attributes = array(); + $this->manyElementsFromXml( + $node, + $context, + 'Attribute', + 'saml', + 'LightSaml\Model\Assertion\Attribute', + 'addAttribute' + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AudienceRestriction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AudienceRestriction.php new file mode 100644 index 0000000000..be098cfb96 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AudienceRestriction.php @@ -0,0 +1,96 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\SamlConstants; + +class AudienceRestriction extends AbstractCondition +{ + /** + * @var string[] + */ + protected $audience = array(); + + /** + * @param string|string[] $audience + */ + public function __construct($audience = array()) + { + if (false == is_array($audience)) { + $audience = array($audience); + } + $this->audience = $audience; + } + + /** + * @param string $audience + * + * @return AudienceRestriction + */ + public function addAudience($audience) + { + $this->audience[] = $audience; + + return $this; + } + + /** + * @return string[] + */ + public function getAllAudience() + { + return $this->audience; + } + + /** + * @param string $value + * + * @return bool + */ + public function hasAudience($value) + { + if (is_array($this->audience)) { + foreach ($this->audience as $a) { + if ($a == $value) { + return true; + } + } + } + + return false; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('AudienceRestriction', SamlConstants::NS_ASSERTION, $parent, $context); + + $this->manyElementsToXml($this->getAllAudience(), $result, $context, 'Audience', SamlConstants::NS_ASSERTION); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'AudienceRestriction', SamlConstants::NS_ASSERTION); + + $this->audience = array(); + $this->manyElementsFromXml($node, $context, 'Audience', 'saml', null, 'addAudience'); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AuthnContext.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AuthnContext.php new file mode 100644 index 0000000000..fffe3181c9 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AuthnContext.php @@ -0,0 +1,154 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\SamlConstants; + +class AuthnContext extends AbstractSamlModel +{ + /** + * @var string|null + */ + protected $authnContextClassRef; + + /** + * @var string|null + */ + protected $authnContextDecl; + + /** + * @var string|null + */ + protected $authnContextDeclRef; + + /** + * @var string|null + */ + protected $authenticatingAuthority; + + /** + * @param string|null $authenticatingAuthority + * + * @return AuthnContext + */ + public function setAuthenticatingAuthority($authenticatingAuthority) + { + $this->authenticatingAuthority = (string) $authenticatingAuthority; + + return $this; + } + + /** + * @return string + */ + public function getAuthenticatingAuthority() + { + return $this->authenticatingAuthority; + } + + /** + * @param null|string $authnContextClassRef + * + * @return AuthnContext + */ + public function setAuthnContextClassRef($authnContextClassRef) + { + $this->authnContextClassRef = (string) $authnContextClassRef; + + return $this; + } + + /** + * @return null|string + */ + public function getAuthnContextClassRef() + { + return $this->authnContextClassRef; + } + + /** + * @param null|string $authnContextDecl + * + * @return AuthnContext + */ + public function setAuthnContextDecl($authnContextDecl) + { + $this->authnContextDecl = (string) $authnContextDecl; + + return $this; + } + + /** + * @return null|string + */ + public function getAuthnContextDecl() + { + return $this->authnContextDecl; + } + + /** + * @param null|string $authnContextDeclRef + * + * @return AuthnContext + */ + public function setAuthnContextDeclRef($authnContextDeclRef) + { + $this->authnContextDeclRef = (string) $authnContextDeclRef; + + return $this; + } + + /** + * @return null|string + */ + public function getAuthnContextDeclRef() + { + return $this->authnContextDeclRef; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('AuthnContext', SamlConstants::NS_ASSERTION, $parent, $context); + + $this->singleElementsToXml( + array('AuthnContextClassRef', 'AuthnContextDecl', 'AuthnContextDeclRef', 'AuthenticatingAuthority'), + $result, + $context, + SamlConstants::NS_ASSERTION + ); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'AuthnContext', SamlConstants::NS_ASSERTION); + + $this->singleElementsFromXml($node, $context, array( + 'AuthnContextClassRef' => array('saml', null), + 'AuthnContextDecl' => array('saml', null), + 'AuthnContextDeclRef' => array('saml', null), + 'AuthenticatingAuthority' => array('saml', null), + )); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AuthnStatement.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AuthnStatement.php new file mode 100644 index 0000000000..e5f63da7e6 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/AuthnStatement.php @@ -0,0 +1,231 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Helper; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\SamlConstants; + +class AuthnStatement extends AbstractStatement +{ + /** + * @var int|null + */ + protected $authnInstant; + + /** + * @var int|null + */ + protected $sessionNotOnOrAfter; + + /** + * @var string|null + */ + protected $sessionIndex; + + /** + * @var AuthnContext + */ + protected $authnContext; + + /** + * @var SubjectLocality + */ + protected $subjectLocality; + + /** + * @param AuthnContext $authnContext + * + * @return AuthnStatement + */ + public function setAuthnContext(AuthnContext $authnContext) + { + $this->authnContext = $authnContext; + + return $this; + } + + /** + * @return \LightSaml\Model\Assertion\AuthnContext + */ + public function getAuthnContext() + { + return $this->authnContext; + } + + /** + * @param int|string|\DateTime $authnInstant + * + * @return AuthnStatement + */ + public function setAuthnInstant($authnInstant) + { + $this->authnInstant = Helper::getTimestampFromValue($authnInstant); + + return $this; + } + + /** + * @return int|null + */ + public function getAuthnInstantTimestamp() + { + return $this->authnInstant; + } + + /** + * @return string|null + */ + public function getAuthnInstantString() + { + if ($this->authnInstant) { + return Helper::time2string($this->authnInstant); + } + + return null; + } + + /** + * @return \DateTime|null + */ + public function getAuthnInstantDateTime() + { + if ($this->authnInstant) { + return new \DateTime('@'.$this->authnInstant); + } + + return null; + } + + /** + * @param null|string $sessionIndex + * + * @return AuthnStatement + */ + public function setSessionIndex($sessionIndex) + { + $this->sessionIndex = $sessionIndex; + + return $this; + } + + /** + * @return null|string + */ + public function getSessionIndex() + { + return $this->sessionIndex; + } + + /** + * @param int|string|\DateTime $sessionNotOnOrAfter + * + * @return AuthnStatement + */ + public function setSessionNotOnOrAfter($sessionNotOnOrAfter) + { + $this->sessionNotOnOrAfter = Helper::getTimestampFromValue($sessionNotOnOrAfter); + + return $this; + } + + /** + * @return int|null + */ + public function getSessionNotOnOrAfterTimestamp() + { + return $this->sessionNotOnOrAfter; + } + + /** + * @return string|null + */ + public function getSessionNotOnOrAfterString() + { + if ($this->sessionNotOnOrAfter) { + return Helper::time2string($this->sessionNotOnOrAfter); + } + + return null; + } + + /** + * @return \DateTime|null + */ + public function getSessionNotOnOrAfterDateTime() + { + if ($this->sessionNotOnOrAfter) { + return new \DateTime('@'.$this->sessionNotOnOrAfter); + } + + return null; + } + + /** + * @param SubjectLocality $subjectLocality + * + * @return AuthnStatement + */ + public function setSubjectLocality($subjectLocality) + { + $this->subjectLocality = $subjectLocality; + + return $this; + } + + /** + * @return \LightSaml\Model\Assertion\SubjectLocality + */ + public function getSubjectLocality() + { + return $this->subjectLocality; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('AuthnStatement', SamlConstants::NS_ASSERTION, $parent, $context); + + $this->attributesToXml( + array('AuthnInstant', 'SessionNotOnOrAfter', 'SessionIndex'), + $result + ); + + $this->singleElementsToXml( + array('SubjectLocality', 'AuthnContext'), + $result, + $context + ); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'AuthnStatement', SamlConstants::NS_ASSERTION); + + $this->attributesFromXml($node, array('AuthnInstant', 'SessionNotOnOrAfter', 'SessionIndex')); + + $this->singleElementsFromXml($node, $context, array( + 'SubjectLocality' => array('saml', 'LightSaml\Model\Assertion\SubjectLocality'), + 'AuthnContext' => array('saml', 'LightSaml\Model\Assertion\AuthnContext'), + )); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Conditions.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Conditions.php new file mode 100644 index 0000000000..a6404b2c57 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Conditions.php @@ -0,0 +1,287 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Helper; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\SamlConstants; + +class Conditions extends AbstractSamlModel +{ + /** + * @var int|null + */ + protected $notBefore; + + /** + * @var int|null + */ + protected $notOnOrAfter; + + /** + * @var array|AbstractCondition[]|AudienceRestriction[]|OneTimeUse[]|ProxyRestriction[] + */ + protected $items = array(); + + /** + * @param AbstractCondition $item + * + * @return Conditions + */ + public function addItem(AbstractCondition $item) + { + $this->items[] = $item; + + return $this; + } + + /** + * @return AbstractCondition[]|AudienceRestriction[]|OneTimeUse[]|ProxyRestriction[]|array + */ + public function getAllItems() + { + return $this->items; + } + + /** + * @return \LightSaml\Model\Assertion\AudienceRestriction[] + */ + public function getAllAudienceRestrictions() + { + $result = array(); + foreach ($this->items as $item) { + if ($item instanceof AudienceRestriction) { + $result[] = $item; + } + } + + return $result; + } + + /** + * @return \LightSaml\Model\Assertion\AudienceRestriction|null + */ + public function getFirstAudienceRestriction() + { + foreach ($this->items as $item) { + if ($item instanceof AudienceRestriction) { + return $item; + } + } + + return null; + } + + /** + * @return \LightSaml\Model\Assertion\OneTimeUse[] + */ + public function getAllOneTimeUses() + { + $result = array(); + foreach ($this->items as $item) { + if ($item instanceof OneTimeUse) { + $result[] = $item; + } + } + + return $result; + } + + /** + * @return \LightSaml\Model\Assertion\OneTimeUse|null + */ + public function getFirstOneTimeUse() + { + foreach ($this->items as $item) { + if ($item instanceof OneTimeUse) { + return $item; + } + } + + return null; + } + + /** + * @return \LightSaml\Model\Assertion\ProxyRestriction[] + */ + public function getAllProxyRestrictions() + { + $result = array(); + foreach ($this->items as $item) { + if ($item instanceof ProxyRestriction) { + $result[] = $item; + } + } + + return $result; + } + + /** + * @return \LightSaml\Model\Assertion\ProxyRestriction|null + */ + public function getFirstProxyRestriction() + { + foreach ($this->items as $item) { + if ($item instanceof ProxyRestriction) { + return $item; + } + } + + return null; + } + + /** + * @param int|string|\DateTime|null $notBefore + * + * @return Conditions + */ + public function setNotBefore($notBefore) + { + $this->notBefore = Helper::getTimestampFromValue($notBefore); + + return $this; + } + + /** + * @return int|null + */ + public function getNotBeforeTimestamp() + { + return $this->notBefore; + } + + /** + * @return int|null + */ + public function getNotBeforeString() + { + if ($this->notBefore) { + return Helper::time2string($this->notBefore); + } + + return null; + } + + /** + * @return \DateTime|null + */ + public function getNotBeforeDateTime() + { + if ($this->notBefore) { + return new \DateTime('@'.$this->notBefore); + } + + return null; + } + + /** + * @param int|null $notOnOrAfter + * + * @return Conditions + */ + public function setNotOnOrAfter($notOnOrAfter) + { + $this->notOnOrAfter = Helper::getTimestampFromValue($notOnOrAfter); + + return $this; + } + + /** + * @return int|null + */ + public function getNotOnOrAfterTimestamp() + { + return $this->notOnOrAfter; + } + + /** + * @return string|null + */ + public function getNotOnOrAfterString() + { + if ($this->notOnOrAfter) { + return Helper::time2string($this->notOnOrAfter); + } + + return null; + } + + /** + * @return \DateTime|null + */ + public function getNotOnOrAfterDateTime() + { + if ($this->notOnOrAfter) { + return new \DateTime('@'.$this->notOnOrAfter); + } + + return null; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('Conditions', SamlConstants::NS_ASSERTION, $parent, $context); + + $this->attributesToXml( + array('NotBefore', 'NotOnOrAfter'), + $result + ); + + foreach ($this->items as $item) { + $item->serialize($result, $context); + } + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'Conditions', SamlConstants::NS_ASSERTION); + + $this->attributesFromXml($node, array('NotBefore', 'NotOnOrAfter')); + + $this->manyElementsFromXml( + $node, + $context, + 'AudienceRestriction', + 'saml', + 'LightSaml\Model\Assertion\AudienceRestriction', + 'addItem' + ); + $this->manyElementsFromXml( + $node, + $context, + 'OneTimeUse', + 'saml', + 'LightSaml\Model\Assertion\OneTimeUse', + 'addItem' + ); + $this->manyElementsFromXml( + $node, + $context, + 'ProxyRestriction', + 'saml', + 'LightSaml\Model\Assertion\ProxyRestriction', + 'addItem' + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/EncryptedAssertionReader.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/EncryptedAssertionReader.php new file mode 100644 index 0000000000..c03cc22143 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/EncryptedAssertionReader.php @@ -0,0 +1,60 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Model\Context\DeserializationContext; +use RobRichards\XMLSecLibs\XMLSecurityKey; + +class EncryptedAssertionReader extends EncryptedElementReader +{ + /** + * @param XMLSecurityKey[] $inputKeys + * @param DeserializationContext $deserializationContext + * + * @return Assertion + */ + public function decryptMultiAssertion(array $inputKeys, DeserializationContext $deserializationContext) + { + $dom = $this->decryptMulti($inputKeys); + + return $this->getAssertionFromDom($dom, $deserializationContext); + } + + /** + * @param XMLSecurityKey $credential + * @param DeserializationContext $deserializationContext + * + * @return Assertion + */ + public function decryptAssertion($credential, DeserializationContext $deserializationContext) + { + $dom = $this->decrypt($credential); + + return $this->getAssertionFromDom($dom, $deserializationContext); + } + + /** + * @param \DOMElement $dom + * @param DeserializationContext $deserializationContext + * + * @return Assertion + */ + protected function getAssertionFromDom(\DOMElement $dom, DeserializationContext $deserializationContext) + { + $deserializationContext->setDocument($dom->ownerDocument); + + $assertion = new Assertion(); + $assertion->deserialize($dom, $deserializationContext); + + return $assertion; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/EncryptedAssertionWriter.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/EncryptedAssertionWriter.php new file mode 100644 index 0000000000..67c487c922 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/EncryptedAssertionWriter.php @@ -0,0 +1,29 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Model\Context\SerializationContext; +use LightSaml\SamlConstants; + +class EncryptedAssertionWriter extends EncryptedElementWriter +{ + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return \DOMElement + */ + protected function createRootElement(\DOMNode $parent, SerializationContext $context) + { + return $this->createElement('saml:EncryptedAssertion', SamlConstants::NS_ASSERTION, $parent, $context); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/EncryptedElement.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/EncryptedElement.php new file mode 100644 index 0000000000..be45d94fa5 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/EncryptedElement.php @@ -0,0 +1,18 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Model\AbstractSamlModel; + +abstract class EncryptedElement extends AbstractSamlModel +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/EncryptedElementReader.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/EncryptedElementReader.php new file mode 100644 index 0000000000..3ebd1a8b44 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/EncryptedElementReader.php @@ -0,0 +1,266 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Credential\CredentialInterface; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Error\LightSamlSecurityException; +use LightSaml\Error\LightSamlXmlException; +use RobRichards\XMLSecLibs\XMLSecurityKey; +use RobRichards\XMLSecLibs\XMLSecEnc; + +class EncryptedElementReader extends EncryptedElement +{ + /** @var XMLSecEnc */ + protected $xmlEnc; + + /** @var XMLSecurityKey */ + protected $symmetricKey; + + /** @var XMLSecurityKey */ + protected $symmetricKeyInfo; + + /** + * @return XMLSecurityKey + */ + public function getSymmetricKey() + { + return $this->symmetricKey; + } + + /** + * @return XMLSecurityKey + */ + public function getSymmetricKeyInfo() + { + return $this->symmetricKeyInfo; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @throws \LogicException + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + throw new \LogicException('EncryptedElementReader can not be used for serialization'); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $list = $context->getXpath()->query('xenc:EncryptedData', $node); + if (0 == $list->length) { + throw new LightSamlXmlException('Missing encrypted data in '); + } + if (1 != $list->length) { + throw new LightSamlXmlException('More than one encrypted data element in '); + } + + /** @var \DOMElement $encryptedData */ + $encryptedData = $list->item(0); + $this->xmlEnc = new XMLSecEnc(); + $this->xmlEnc->setNode($encryptedData); + $this->xmlEnc->type = $encryptedData->getAttribute('Type'); + + $this->symmetricKey = $this->loadSymmetricKey(); + + $this->symmetricKeyInfo = $this->loadSymmetricKeyInfo($this->symmetricKey); + } + + /** + * @param XMLSecurityKey[] $inputKeys + * + * @throws \LogicException + * @throws \LightSaml\Error\LightSamlXmlException + * @throws \LightSaml\Error\LightSamlSecurityException + * + * @return \DOMElement + */ + public function decryptMulti(array $inputKeys) + { + /** @var \LogicException $lastException */ + $lastException = null; + + foreach ($inputKeys as $key) { + if ($key instanceof CredentialInterface) { + $key = $key->getPrivateKey(); + } + if (false == $key instanceof XMLSecurityKey) { + throw new \InvalidArgumentException('Expected XMLSecurityKey'); + } + + try { + return $this->decrypt($key); + } catch (\Exception $ex) { + $lastException = $ex; + } + } + + if ($lastException) { + throw $lastException; + } + + throw new LightSamlSecurityException('No key provided for decryption'); + } + + /** + * @param XMLSecurityKey $inputKey + * + * @throws \LogicException + * @throws \LightSaml\Error\LightSamlXmlException + * @throws \LightSaml\Error\LightSamlSecurityException + * + * @return \DOMElement + */ + public function decrypt(XMLSecurityKey $inputKey) + { + $this->symmetricKey = $this->loadSymmetricKey(); + $this->symmetricKeyInfo = $this->loadSymmetricKeyInfo($this->symmetricKey); + + if ($this->symmetricKeyInfo->isEncrypted) { + $this->decryptSymmetricKey($inputKey); + } else { + $this->symmetricKey = $inputKey; + } + + $decrypted = $this->decryptCipher(); + + $result = $this->buildXmlElement($decrypted); + + return $result; + } + + /** + * @param string $decrypted + * + * @return \DOMElement + */ + protected function buildXmlElement($decrypted) + { + /* + * This is a workaround for the case where only a subset of the XML + * tree was serialized for encryption. In that case, we may miss the + * namespaces needed to parse the XML. + */ + $xml = sprintf( + '%s', + $decrypted + ); + $newDoc = new \DOMDocument(); + if (false == @$newDoc->loadXML($xml)) { + throw new LightSamlXmlException('Failed to parse decrypted XML. Maybe the wrong sharedkey was used?'); + } + $decryptedElement = $newDoc->firstChild->firstChild; + if (null == $decryptedElement) { + throw new LightSamlSecurityException('Missing encrypted element.'); + } + + if (false == $decryptedElement instanceof \DOMElement) { + throw new LightSamlXmlException('Decrypted element was not actually a DOMElement.'); + } + + return $decryptedElement; + } + + /** + * @return string + * + * @throws \Exception + */ + protected function decryptCipher() + { + $decrypted = $this->xmlEnc->decryptNode($this->symmetricKey, false); + if (false == is_string($decrypted)) { + throw new \LogicException('Expected decrypted string'); + } + + return $decrypted; + } + + /** + * @param XMLSecurityKey $inputKey + * + * @throws \Exception + */ + protected function decryptSymmetricKey(XMLSecurityKey $inputKey) + { + /** @var XMLSecEnc $encKey */ + $encKey = $this->symmetricKeyInfo->encryptedCtx; + $this->symmetricKeyInfo->key = $inputKey->key; + + $keySize = $this->symmetricKey->getSymmetricKeySize(); + if (null === $keySize) { + // To protect against "key oracle" attacks, we need to be able to create a + // symmetric key, and for that we need to know the key size. + throw new LightSamlSecurityException(sprintf( + "Unknown key size for encryption algorithm: '%s'", + $this->symmetricKey->type + )); + } + + /** @var string $key */ + $key = $encKey->decryptKey($this->symmetricKeyInfo); + if (false == is_string($key)) { + throw new \LogicException('Expected string'); + } + if (strlen($key) != $keySize) { + throw new LightSamlSecurityException(sprintf( + "Unexpected key size of '%s' bits for encryption algorithm '%s', expected '%s' bits size", + strlen($key) * 8, + $this->symmetricKey->type, + $keySize + )); + } + + $this->symmetricKey->loadkey($key); + } + + /** + * @return XMLSecurityKey + * + * @throws \LightSaml\Error\LightSamlXmlException + */ + protected function loadSymmetricKey() + { + $symmetricKey = $this->xmlEnc->locateKey(); + if (false == $symmetricKey) { + throw new LightSamlXmlException('Could not locate key algorithm in encrypted data'); + } + + return $symmetricKey; + } + + /** + * @param XMLSecurityKey $symmetricKey + * + * @throws \LightSaml\Error\LightSamlXmlException + * + * @return XMLSecurityKey + */ + protected function loadSymmetricKeyInfo(XMLSecurityKey $symmetricKey) + { + $symmetricKeyInfo = $this->xmlEnc->locateKeyInfo($symmetricKey); + if (false == $symmetricKeyInfo) { + throw new LightSamlXmlException('Could not locate for the encrypted key'); + } + + return $symmetricKeyInfo; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/EncryptedElementWriter.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/EncryptedElementWriter.php new file mode 100644 index 0000000000..511c2a4f01 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/EncryptedElementWriter.php @@ -0,0 +1,124 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Error\LightSamlException; +use LightSaml\Model\AbstractSamlModel; +use RobRichards\XMLSecLibs\XMLSecurityKey; +use RobRichards\XMLSecLibs\XMLSecEnc; + +abstract class EncryptedElementWriter extends EncryptedElement +{ + /** @var \DOMElement */ + protected $encryptedElement; + + /** @var string */ + protected $blockEncryptionAlgorithm = XMLSecurityKey::AES128_CBC; + + /** @var string */ + protected $keyTransportEncryption = XMLSecurityKey::RSA_1_5; + + /** + * @param string $blockEncryptionAlgorithm + * @param string $keyTransportEncryption + */ + public function __construct($blockEncryptionAlgorithm = XMLSecurityKey::AES128_CBC, $keyTransportEncryption = XMLSecurityKey::RSA_1_5) + { + $this->blockEncryptionAlgorithm = $blockEncryptionAlgorithm; + $this->keyTransportEncryption = $keyTransportEncryption; + } + + /** + * @param AbstractSamlModel $object + * @param XMLSecurityKey $key + * + * @return SerializationContext + */ + public function encrypt(AbstractSamlModel $object, XMLSecurityKey $key) + { + $oldKey = $key; + $key = new XMLSecurityKey($this->keyTransportEncryption, ['type' => 'public']); + $key->loadKey($oldKey->key); + + $serializationContext = new SerializationContext(); + $object->serialize($serializationContext->getDocument(), $serializationContext); + + $enc = new XMLSecEnc(); + $enc->setNode($serializationContext->getDocument()->firstChild); + $enc->type = XMLSecEnc::Element; + + switch ($key->type) { + case XMLSecurityKey::TRIPLEDES_CBC: + case XMLSecurityKey::AES128_CBC: + case XMLSecurityKey::AES192_CBC: + case XMLSecurityKey::AES256_CBC: + $symmetricKey = $key; + break; + + case XMLSecurityKey::RSA_1_5: + case XMLSecurityKey::RSA_SHA1: + case XMLSecurityKey::RSA_SHA256: + case XMLSecurityKey::RSA_SHA384: + case XMLSecurityKey::RSA_SHA512: + case XMLSecurityKey::RSA_OAEP_MGF1P: + $symmetricKey = new XMLSecurityKey($this->blockEncryptionAlgorithm); + $symmetricKey->generateSessionKey(); + + $enc->encryptKey($key, $symmetricKey); + + break; + + default: + throw new LightSamlException(sprintf('Unknown key type for encryption: "%s"', $key->type)); + } + + $this->encryptedElement = $enc->encryptNode($symmetricKey); + + return $serializationContext; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return \DOMElement + */ + abstract protected function createRootElement(\DOMNode $parent, SerializationContext $context); + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + if (null === $this->encryptedElement) { + throw new LightSamlException('Encrypted element missing'); + } + + $root = $this->createRootElement($parent, $context); + + $root->appendChild($context->getDocument()->importNode($this->encryptedElement, true)); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + throw new \LogicException('EncryptedElementWriter can not be used for deserialization'); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Issuer.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Issuer.php new file mode 100644 index 0000000000..2e76e71fab --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Issuer.php @@ -0,0 +1,23 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +class Issuer extends AbstractNameID +{ + /** + * @return string + */ + protected function getElementName() + { + return 'Issuer'; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/NameID.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/NameID.php new file mode 100644 index 0000000000..7087164b8e --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/NameID.php @@ -0,0 +1,23 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +class NameID extends AbstractNameID +{ + /** + * @return string + */ + protected function getElementName() + { + return 'NameID'; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/OneTimeUse.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/OneTimeUse.php new file mode 100644 index 0000000000..95ee79d112 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/OneTimeUse.php @@ -0,0 +1,39 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\SamlConstants; + +class OneTimeUse extends AbstractCondition +{ + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $this->createElement('OneTimeUse', SamlConstants::NS_ASSERTION, $parent, $context); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'OneTimeUse', SamlConstants::NS_ASSERTION); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/ProxyRestriction.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/ProxyRestriction.php new file mode 100644 index 0000000000..53c6572bac --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/ProxyRestriction.php @@ -0,0 +1,110 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\SamlConstants; + +class ProxyRestriction extends AbstractCondition +{ + /** + * @var int|null + */ + protected $count; + + /** + * @var string[]|null + */ + protected $audience; + + /** + * @param int $count + * @param string[] $audience + */ + public function __construct($count = null, array $audience = null) + { + $this->count = $count; + $this->audience = $audience; + } + + /** + * @param string $audience + * + * @return ProxyRestriction + */ + public function addAudience($audience) + { + if (false == is_array($this->audience)) { + $this->audience = array(); + } + $this->audience[] = (string) $audience; + + return $this; + } + + /** + * @return null|\string[] + */ + public function getAllAudience() + { + return $this->audience; + } + + /** + * @param int|null $count + * + * @return ProxyRestriction + */ + public function setCount($count) + { + $this->count = null !== $count ? intval($count) : null; + + return $this; + } + + /** + * @return int|null + */ + public function getCount() + { + return $this->count; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('ProxyRestriction', SamlConstants::NS_ASSERTION, $parent, $context); + + $this->attributesToXml(array('count'), $result); + + $this->manyElementsToXml($this->getAllAudience(), $result, $context, 'Audience'); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'ProxyRestriction', SamlConstants::NS_ASSERTION); + + $this->attributesFromXml($node, array('count')); + + $this->manyElementsFromXml($node, $context, 'Audience', 'saml', null, 'addAudience'); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Subject.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Subject.php new file mode 100644 index 0000000000..503db66da3 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/Subject.php @@ -0,0 +1,134 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\SamlConstants; + +class Subject extends AbstractSamlModel +{ + /** @var NameID */ + protected $nameId; + + /** @var SubjectConfirmation[] */ + protected $subjectConfirmation = array(); + + /** + * @param NameID $nameId + * + * @return Subject + */ + public function setNameID(NameID $nameId = null) + { + $this->nameId = $nameId; + + return $this; + } + + /** + * @return \LightSaml\Model\Assertion\NameID + */ + public function getNameID() + { + return $this->nameId; + } + + /** + * @param SubjectConfirmation $subjectConfirmation + * + * @return Subject + */ + public function addSubjectConfirmation(SubjectConfirmation $subjectConfirmation) + { + $this->subjectConfirmation[] = $subjectConfirmation; + + return $this; + } + + /** + * @return SubjectConfirmation[] + */ + public function getAllSubjectConfirmations() + { + return $this->subjectConfirmation; + } + + /** + * @return SubjectConfirmation|null + */ + public function getFirstSubjectConfirmation() + { + if (is_array($this->subjectConfirmation) && isset($this->subjectConfirmation[0])) { + return $this->subjectConfirmation[0]; + } + + return; + } + + /** + * Returns array of containing a Method of urn:oasis:names:tc:SAML:2.0:cm:bearer. + * + * @return SubjectConfirmation[] + */ + public function getBearerConfirmations() + { + $result = array(); + if ($this->getAllSubjectConfirmations()) { + foreach ($this->getAllSubjectConfirmations() as $confirmation) { + if (SamlConstants::CONFIRMATION_METHOD_BEARER == $confirmation->getMethod()) { + $result[] = $confirmation; + break; + } + } + } + + return $result; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('Subject', SamlConstants::NS_ASSERTION, $parent, $context); + + $this->singleElementsToXml(array('NameID'), $result, $context); + $this->manyElementsToXml($this->getAllSubjectConfirmations(), $result, $context, null); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'Subject', SamlConstants::NS_ASSERTION); + + $this->singleElementsFromXml($node, $context, array( + 'NameID' => array('saml', 'LightSaml\Model\Assertion\NameID'), + )); + + $this->manyElementsFromXml( + $node, + $context, + 'SubjectConfirmation', + 'saml', + 'LightSaml\Model\Assertion\SubjectConfirmation', + 'addSubjectConfirmation' + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/SubjectConfirmation.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/SubjectConfirmation.php new file mode 100644 index 0000000000..ac2b3aeed0 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/SubjectConfirmation.php @@ -0,0 +1,148 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\SamlConstants; + +class SubjectConfirmation extends AbstractSamlModel +{ + /** @var string */ + protected $method; + + /** @var NameID|null */ + protected $nameId; + + /** @var EncryptedElement|null */ + protected $encryptedId; + + /** @var SubjectConfirmationData|null */ + protected $subjectConfirmationData; + + /** + * @param string $method + * + * @return SubjectConfirmation + */ + public function setMethod($method) + { + $this->method = (string) $method; + + return $this; + } + + /** + * @return string + */ + public function getMethod() + { + return $this->method; + } + + /** + * @param EncryptedElement|null $encryptedId + * + * @return SubjectConfirmation + */ + public function setEncryptedId(EncryptedElement $encryptedId = null) + { + $this->encryptedId = $encryptedId; + + return $this; + } + + /** + * @return EncryptedElement|null + */ + public function getEncryptedId() + { + return $this->encryptedId; + } + + /** + * @param NameID|null $nameId + * + * @return SubjectConfirmation + */ + public function setNameID(NameID $nameId = null) + { + $this->nameId = $nameId; + + return $this; + } + + /** + * @return \LightSaml\Model\Assertion\NameID|null + */ + public function getNameID() + { + return $this->nameId; + } + + /** + * @param SubjectConfirmationData|null $subjectConfirmationData + * + * @return SubjectConfirmation + */ + public function setSubjectConfirmationData(SubjectConfirmationData $subjectConfirmationData = null) + { + $this->subjectConfirmationData = $subjectConfirmationData; + + return $this; + } + + /** + * @return SubjectConfirmationData|null + */ + public function getSubjectConfirmationData() + { + return $this->subjectConfirmationData; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('SubjectConfirmation', SamlConstants::NS_ASSERTION, $parent, $context); + + $this->attributesToXml(array('Method'), $result); + + $this->singleElementsToXml( + array('NameID', 'EncryptedID', 'SubjectConfirmationData'), + $result, + $context + ); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'SubjectConfirmation', SamlConstants::NS_ASSERTION); + + $this->attributesFromXml($node, array('Method')); + + $this->singleElementsFromXml($node, $context, array( + 'NameID' => array('saml', 'LightSaml\Model\Assertion\NameID'), + 'EncryptedID' => array('saml', 'LightSaml\Model\Assertion\EncryptedID'), + 'SubjectConfirmationData' => array('saml', 'LightSaml\Model\Assertion\SubjectConfirmationData'), + )); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/SubjectConfirmationData.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/SubjectConfirmationData.php new file mode 100644 index 0000000000..02c2f7b284 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/SubjectConfirmationData.php @@ -0,0 +1,213 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Helper; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\SamlConstants; + +class SubjectConfirmationData extends AbstractSamlModel +{ + /** @var int|null */ + protected $notBefore; + + /** @var int|null */ + protected $notOnOrAfter; + + /** @var string|null */ + protected $address; + + /** @var string|null */ + protected $inResponseTo; + + /** @var string|null */ + protected $recipient; + + /** + * @param null|string $address + * + * @return SubjectConfirmationData + */ + public function setAddress($address) + { + $this->address = (string) $address; + + return $this; + } + + /** + * @return null|string + */ + public function getAddress() + { + return $this->address; + } + + /** + * @param null|string $inResponseTo + * + * @return SubjectConfirmationData + */ + public function setInResponseTo($inResponseTo) + { + $this->inResponseTo = (string) $inResponseTo; + + return $this; + } + + /** + * @return null|string + */ + public function getInResponseTo() + { + return $this->inResponseTo; + } + + /** + * @param int|string|\DateTime $notBefore + * + * @return SubjectConfirmationData + */ + public function setNotBefore($notBefore) + { + $this->notBefore = Helper::getTimestampFromValue($notBefore); + + return $this; + } + + /** + * @return int|null + */ + public function getNotBeforeTimestamp() + { + return $this->notBefore; + } + + /** + * @return string|null + */ + public function getNotBeforeString() + { + if ($this->notBefore) { + return Helper::time2string($this->notBefore); + } + + return; + } + + /** + * @return \DateTime|null + */ + public function getNotBeforeDateTime() + { + if ($this->notBefore) { + return new \DateTime('@'.$this->notBefore); + } + + return; + } + + /** + * @param int|string|\DateTime $notOnOrAfter + * + * @return SubjectConfirmationData + */ + public function setNotOnOrAfter($notOnOrAfter) + { + $this->notOnOrAfter = Helper::getTimestampFromValue($notOnOrAfter); + + return $this; + } + + /** + * @return int|null + */ + public function getNotOnOrAfterTimestamp() + { + return $this->notOnOrAfter; + } + + /** + * @return string|null + */ + public function getNotOnOrAfterString() + { + if ($this->notOnOrAfter) { + return Helper::time2string($this->notOnOrAfter); + } + + return; + } + + /** + * @return \DateTime|null + */ + public function getNotOnOrAfterDateTime() + { + if ($this->notOnOrAfter) { + return new \DateTime('@'.$this->notOnOrAfter); + } + + return; + } + + /** + * @param null|string $recipient + * + * @return SubjectConfirmationData + */ + public function setRecipient($recipient) + { + $this->recipient = (string) $recipient; + + return $this; + } + + /** + * @return null|string + */ + public function getRecipient() + { + return $this->recipient; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('SubjectConfirmationData', SamlConstants::NS_ASSERTION, $parent, $context); + + $this->attributesToXml( + array('InResponseTo', 'NotBefore', 'NotOnOrAfter', 'Address', 'Recipient'), + $result + ); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'SubjectConfirmationData', SamlConstants::NS_ASSERTION); + + $this->attributesFromXml($node, array( + 'InResponseTo', 'NotBefore', 'NotOnOrAfter', 'Address', 'Recipient', + )); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/SubjectLocality.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/SubjectLocality.php new file mode 100644 index 0000000000..c9d0563680 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Assertion/SubjectLocality.php @@ -0,0 +1,94 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Assertion; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\SamlConstants; + +class SubjectLocality extends AbstractSamlModel +{ + /** + * @var string + */ + protected $address; + + /** + * @var string + */ + protected $dnsName; + + /** + * @param string $address + * + * @return SubjectLocality + */ + public function setAddress($address) + { + $this->address = $address; + + return $this; + } + + /** + * @return string + */ + public function getAddress() + { + return $this->address; + } + + /** + * @param string $dnsName + * + * @return SubjectLocality + */ + public function setDNSName($dnsName) + { + $this->dnsName = $dnsName; + + return $this; + } + + /** + * @return string + */ + public function getDNSName() + { + return $this->dnsName; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('SubjectLocality', SamlConstants::NS_ASSERTION, $parent, $context); + + $this->attributesToXml(array('Address', 'DNSName'), $result); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'SubjectLocality', SamlConstants::NS_ASSERTION); + + $this->attributesFromXml($node, array('Address', 'DNSName')); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Context/DeserializationContext.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Context/DeserializationContext.php new file mode 100644 index 0000000000..9c0fddc480 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Context/DeserializationContext.php @@ -0,0 +1,69 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Context; + +use LightSaml\SamlConstants; +use RobRichards\XMLSecLibs\XMLSecEnc; + +class DeserializationContext +{ + /** @var \DOMDocument */ + private $document; + + /** @var \DOMXPath */ + private $xpath; + + /** + * @param \DOMDocument $document + */ + public function __construct(\DOMDocument $document = null) + { + $this->document = $document ? $document : new \DOMDocument(); + } + + /** + * @return \DOMDocument + */ + public function getDocument() + { + return $this->document; + } + + /** + * @param \DOMDocument $document + * + * @return DeserializationContext + */ + public function setDocument(\DOMDocument $document) + { + $this->document = $document; + + return $this; + } + + /** + * @return \DOMXPath + */ + public function getXpath() + { + if (null == $this->xpath) { + $this->xpath = new \DOMXPath($this->document); + $this->xpath->registerNamespace('saml', SamlConstants::NS_ASSERTION); + $this->xpath->registerNamespace('samlp', SamlConstants::NS_PROTOCOL); + $this->xpath->registerNamespace('md', SamlConstants::NS_METADATA); + $this->xpath->registerNamespace('ds', SamlConstants::NS_XMLDSIG); + $this->xpath->registerNamespace('xenc', XMLSecEnc::XMLENCNS); + } + + return $this->xpath; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Context/SerializationContext.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Context/SerializationContext.php new file mode 100644 index 0000000000..e26bf79a5a --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Context/SerializationContext.php @@ -0,0 +1,42 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Context; + +class SerializationContext +{ + /** @var \DOMDocument */ + protected $document; + + /** + * @param \DOMDocument $document + */ + public function __construct(\DOMDocument $document = null) + { + $this->document = $document ? $document : new \DOMDocument(); + } + + /** + * @param \DOMDocument $document + */ + public function setDocument(\DOMDocument $document) + { + $this->document = $document; + } + + /** + * @return \DOMDocument + */ + public function getDocument() + { + return $this->document; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/AssertionConsumerService.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/AssertionConsumerService.php new file mode 100644 index 0000000000..bbfc3a7172 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/AssertionConsumerService.php @@ -0,0 +1,35 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Metadata; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\SamlConstants; + +class AssertionConsumerService extends IndexedEndpoint +{ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('AssertionConsumerService', SamlConstants::NS_METADATA, $parent, $context); + parent::serialize($result, $context); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'AssertionConsumerService', SamlConstants::NS_METADATA); + parent::deserialize($node, $context); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/ContactPerson.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/ContactPerson.php new file mode 100644 index 0000000000..9d0a61d2a5 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/ContactPerson.php @@ -0,0 +1,203 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Metadata; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\SamlConstants; + +class ContactPerson extends AbstractSamlModel +{ + const TYPE_TECHNICAL = 'technical'; + const TYPE_SUPPORT = 'support'; + const TYPE_ADMINISTRATIVE = 'administrative'; + const TYPE_BILLING = 'billing'; + const TYPE_OTHER = 'other'; + + /** @var string */ + protected $contactType; + + /** @var string|null */ + protected $company; + + /** @var string|null */ + protected $givenName; + + /** @var string|null */ + protected $surName; + + /** @var string|null */ + protected $emailAddress; + + /** @var string|null */ + protected $telephoneNumber; + + /** + * @param string $contactType + * + * @return ContactPerson + */ + public function setContactType($contactType) + { + $this->contactType = (string) $contactType; + + return $this; + } + + /** + * @return string + */ + public function getContactType() + { + return $this->contactType; + } + + /** + * @param null|string $company + * + * @return ContactPerson + */ + public function setCompany($company) + { + $this->company = $company; + + return $this; + } + + /** + * @return null|string + */ + public function getCompany() + { + return $this->company; + } + + /** + * @param null|string $emailAddress + * + * @return ContactPerson + */ + public function setEmailAddress($emailAddress) + { + $this->emailAddress = $emailAddress; + + return $this; + } + + /** + * @return null|string + */ + public function getEmailAddress() + { + return $this->emailAddress; + } + + /** + * @param null|string $givenName + * + * @return ContactPerson + */ + public function setGivenName($givenName) + { + $this->givenName = $givenName; + + return $this; + } + + /** + * @return null|string + */ + public function getGivenName() + { + return $this->givenName; + } + + /** + * @param null|string $surName + * + * @return ContactPerson + */ + public function setSurName($surName) + { + $this->surName = $surName; + + return $this; + } + + /** + * @return null|string + */ + public function getSurName() + { + return $this->surName; + } + + /** + * @param null|string $telephoneNumber + * + * @return ContactPerson + */ + public function setTelephoneNumber($telephoneNumber) + { + $this->telephoneNumber = $telephoneNumber; + + return $this; + } + + /** + * @return null|string + */ + public function getTelephoneNumber() + { + return $this->telephoneNumber; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('ContactPerson', SamlConstants::NS_METADATA, $parent, $context); + + $this->attributesToXml(array('contactType'), $result); + + $this->singleElementsToXml( + array('Company', 'GivenName', 'SurName', 'EmailAddress', 'TelephoneNumber'), + $result, + $context, + SamlConstants::NS_METADATA + ); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'ContactPerson', SamlConstants::NS_METADATA); + + $this->attributesFromXml($node, array('contactType')); + + $this->singleElementsFromXml($node, $context, array( + 'Company' => array('md', null), + 'GivenName' => array('md', null), + 'SurName' => array('md', null), + 'EmailAddress' => array('md', null), + 'TelephoneNumber' => array('md', null), + )); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/Endpoint.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/Endpoint.php new file mode 100644 index 0000000000..ec3f157a2c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/Endpoint.php @@ -0,0 +1,116 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Metadata; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; + +abstract class Endpoint extends AbstractSamlModel +{ + /** @var string */ + protected $binding; + + /** @var string */ + protected $location; + + /** @var string|null */ + protected $responseLocation; + + /** + * @param string $location + * @param string $binding + */ + public function __construct($location = null, $binding = null) + { + $this->location = $location; + $this->binding = $binding; + } + + /** + * @param string $binding + * + * @return Endpoint + */ + public function setBinding($binding) + { + $this->binding = (string) $binding; + + return $this; + } + + /** + * @return string + */ + public function getBinding() + { + return $this->binding; + } + + /** + * @param string $location + * + * @return Endpoint + */ + public function setLocation($location) + { + $this->location = (string) $location; + + return $this; + } + + /** + * @return string + */ + public function getLocation() + { + return $this->location; + } + + /** + * @param null|string $responseLocation + * + * @return Endpoint + */ + public function setResponseLocation($responseLocation) + { + $this->responseLocation = $responseLocation ? (string) $responseLocation : null; + + return $this; + } + + /** + * @return null|string + */ + public function getResponseLocation() + { + return $this->responseLocation; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $this->attributesToXml(array('Binding', 'Location', 'ResponseLocation'), $parent); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->attributesFromXml($node, array('Binding', 'Location', 'ResponseLocation')); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/EndpointReference.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/EndpointReference.php new file mode 100644 index 0000000000..6378a6f0dd --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/EndpointReference.php @@ -0,0 +1,60 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Metadata; + +class EndpointReference +{ + /** @var EntityDescriptor */ + protected $entityDescriptor; + + /** @var RoleDescriptor */ + protected $descriptor; + + /** @var Endpoint */ + protected $endpoint; + + /** + * @param EntityDescriptor $entityDescriptor + * @param RoleDescriptor $descriptor + * @param Endpoint $endpoint + */ + public function __construct(EntityDescriptor $entityDescriptor, RoleDescriptor $descriptor, Endpoint $endpoint) + { + $this->entityDescriptor = $entityDescriptor; + $this->descriptor = $descriptor; + $this->endpoint = $endpoint; + } + + /** + * @return EntityDescriptor + */ + public function getEntityDescriptor() + { + return $this->entityDescriptor; + } + + /** + * @return RoleDescriptor + */ + public function getDescriptor() + { + return $this->descriptor; + } + + /** + * @return Endpoint + */ + public function getEndpoint() + { + return $this->endpoint; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/EntitiesDescriptor.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/EntitiesDescriptor.php new file mode 100644 index 0000000000..88dfa21bfd --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/EntitiesDescriptor.php @@ -0,0 +1,339 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Metadata; + +use LightSaml\Helper; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\XmlDSig\Signature; +use LightSaml\SamlConstants; + +class EntitiesDescriptor extends Metadata +{ + /** @var int */ + protected $validUntil; + + /** @var string */ + protected $cacheDuration; + + /** @var string */ + protected $id; + + /** @var string */ + protected $name; + + /** @var Signature */ + protected $signature; + + /** @var EntitiesDescriptor[]|EntityDescriptor[] */ + protected $items = array(); + + /** + * @param string $filename + * + * @return EntitiesDescriptor + */ + public static function load($filename) + { + return self::loadXml(file_get_contents($filename)); + } + + /** + * @param string $xml + * + * @return EntitiesDescriptor + */ + public static function loadXml($xml) + { + $context = new DeserializationContext(); + $context->getDocument()->loadXML($xml); + $ed = new self(); + $ed->deserialize($context->getDocument(), $context); + + return $ed; + } + + /** + * @param string $cacheDuration + * + * @return EntitiesDescriptor + * + * @throws \InvalidArgumentException + */ + public function setCacheDuration($cacheDuration) + { + Helper::validateDurationString($cacheDuration); + + $this->cacheDuration = $cacheDuration; + + return $this; + } + + /** + * @return string + */ + public function getCacheDuration() + { + return $this->cacheDuration; + } + + /** + * @param string $id + * + * @return EntitiesDescriptor + */ + public function setID($id) + { + $this->id = (string) $id; + + return $this; + } + + /** + * @return string + */ + public function getID() + { + return $this->id; + } + + /** + * @param string $name + * + * @return EntitiesDescriptor + */ + public function setName($name) + { + $this->name = (string) $name; + + return $this; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param \LightSaml\Model\XmlDSig\Signature $signature + * + * @return EntitiesDescriptor + */ + public function setSignature(Signature $signature) + { + $this->signature = $signature; + + return $this; + } + + /** + * @return \LightSaml\Model\XmlDSig\Signature + */ + public function getSignature() + { + return $this->signature; + } + + /** + * @param int|string $validUntil + * + * @return EntitiesDescriptor + * + * @throws \InvalidArgumentException + */ + public function setValidUntil($validUntil) + { + $value = Helper::getTimestampFromValue($validUntil); + if ($value < 0) { + throw new \InvalidArgumentException('Invalid validUntil'); + } + $this->validUntil = $value; + + return $this; + } + + /** + * @return string + */ + public function getValidUntilString() + { + if ($this->validUntil) { + return Helper::time2string($this->validUntil); + } + + return null; + } + + /** + * @return int + */ + public function getValidUntilTimestamp() + { + return $this->validUntil; + } + + /** + * @return \DateTime|null + */ + public function getValidUntilDateTime() + { + if ($this->validUntil) { + return new \DateTime('@'.$this->validUntil); + } + + return null; + } + + /** + * @param EntitiesDescriptor|EntityDescriptor $item + * + * @return EntitiesDescriptor + * + * @throws \InvalidArgumentException + */ + public function addItem($item) + { + if (false == $item instanceof self && false == $item instanceof EntityDescriptor) { + throw new \InvalidArgumentException('Expected EntitiesDescriptor or EntityDescriptor'); + } + if ($item === $this) { + throw new \InvalidArgumentException('Circular reference detected'); + } + if ($item instanceof self) { + if ($item->containsItem($this)) { + throw new \InvalidArgumentException('Circular reference detected'); + } + } + $this->items[] = $item; + + return $this; + } + + /** + * @param EntitiesDescriptor|EntityDescriptor $item + * + * @return bool + * + * @throws \InvalidArgumentException + */ + public function containsItem($item) + { + if (false == $item instanceof self && false == $item instanceof EntityDescriptor) { + throw new \InvalidArgumentException('Expected EntitiesDescriptor or EntityDescriptor'); + } + foreach ($this->items as $i) { + if ($i === $item) { + return true; + } + if ($i instanceof self) { + if ($i->containsItem($item)) { + return true; + } + } + } + + return false; + } + + /** + * @return EntitiesDescriptor[]|EntityDescriptor[] + */ + public function getAllItems() + { + return $this->items; + } + + /** + * @return EntityDescriptor[] + */ + public function getAllEntityDescriptors() + { + $result = array(); + foreach ($this->items as $item) { + if ($item instanceof self) { + $result = array_merge($result, $item->getAllEntityDescriptors()); + } else { + $result[] = $item; + } + } + + return $result; + } + + /** + * @param string $entityId + * + * @return EntityDescriptor|null + */ + public function getByEntityId($entityId) + { + foreach ($this->getAllEntityDescriptors() as $entityDescriptor) { + if ($entityDescriptor->getEntityID() == $entityId) { + return $entityDescriptor; + } + } + + return null; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('EntitiesDescriptor', SamlConstants::NS_METADATA, $parent, $context); + + $this->attributesToXml(array('validUntil', 'cacheDuration', 'ID', 'Name'), $result); + + $this->singleElementsToXml(array('Signature'), $result, $context); + + $this->manyElementsToXml($this->getAllItems(), $result, $context); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'EntitiesDescriptor', SamlConstants::NS_METADATA); + + $this->attributesFromXml($node, array('validUntil', 'cacheDuration', 'ID', 'Name')); + + $this->singleElementsFromXml($node, $context, array( + 'Signature' => array('ds', 'LightSaml\Model\XmlDSig\SignatureXmlReader'), + )); + + $this->manyElementsFromXml( + $node, + $context, + 'EntityDescriptor', + 'md', + 'LightSaml\Model\Metadata\EntityDescriptor', + 'addItem' + ); + $this->manyElementsFromXml( + $node, + $context, + 'EntitiesDescriptor', + 'md', + 'LightSaml\Model\Metadata\EntitiesDescriptor', + 'addItem' + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/EntityDescriptor.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/EntityDescriptor.php new file mode 100644 index 0000000000..517d52f45c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/EntityDescriptor.php @@ -0,0 +1,500 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Metadata; + +use LightSaml\Helper; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\XmlDSig\Signature; +use LightSaml\SamlConstants; + +class EntityDescriptor extends Metadata +{ + /** @var string */ + protected $entityID; + + /** @var int|null */ + protected $validUntil; + + /** @var string|null */ + protected $cacheDuration; + + /** @var string|null */ + protected $id; + + /** @var Signature|null */ + protected $signature; + + /** @var IdpSsoDescriptor[]|SpSsoDescriptor[] */ + protected $items; + + /** @var Organization[]|null */ + protected $organizations; + + /** @var ContactPerson[]|null */ + protected $contactPersons; + + /** + * @param string $filename + * + * @return EntityDescriptor + */ + public static function load($filename) + { + return self::loadXml(file_get_contents($filename)); + } + + /** + * @param string $xml + * + * @return EntityDescriptor + */ + public static function loadXml($xml) + { + $context = new DeserializationContext(); + $context->getDocument()->loadXML($xml); + $ed = new self(); + $ed->deserialize($context->getDocument(), $context); + + return $ed; + } + + /** + * @param string|null $entityId + * @param array $items + */ + public function __construct($entityId = null, array $items = array()) + { + $this->entityID = $entityId; + $this->items = $items; + } + + /** + * @param ContactPerson $contactPerson + * + * @return EntityDescriptor + */ + public function addContactPerson(ContactPerson $contactPerson) + { + if (false == is_array($this->contactPersons)) { + $this->contactPersons = array(); + } + $this->contactPersons[] = $contactPerson; + + return $this; + } + + /** + * @return ContactPerson[]|null + */ + public function getAllContactPersons() + { + return $this->contactPersons; + } + + /** + * @return ContactPerson|null + */ + public function getFirstContactPerson() + { + if (is_array($this->contactPersons) && isset($this->contactPersons[0])) { + return $this->contactPersons[0]; + } + + return null; + } + + /** + * @param \LightSaml\Model\Metadata\Organization $organization + * + * @return EntityDescriptor + */ + public function addOrganization(Organization $organization) + { + if (false == is_array($this->organizations)) { + $this->organizations = array(); + } + $this->organizations[] = $organization; + + return $this; + } + + /** + * @return Organization[]|null + */ + public function getAllOrganizations() + { + return $this->organizations; + } + + /** + * @return \LightSaml\Model\Metadata\Organization|null + */ + public function getFirstOrganization() + { + if (is_array($this->organizations) && isset($this->organizations[0])) { + return $this->organizations[0]; + } + + return null; + } + + /** + * @param string|null $cacheDuration + * + * @throws \InvalidArgumentException + * + * @return EntityDescriptor + */ + public function setCacheDuration($cacheDuration) + { + Helper::validateDurationString($cacheDuration); + + $this->cacheDuration = $cacheDuration; + + return $this; + } + + /** + * @return string|null + */ + public function getCacheDuration() + { + return $this->cacheDuration; + } + + /** + * @param string $entityID + * + * @return EntityDescriptor + */ + public function setEntityID($entityID) + { + $this->entityID = (string) $entityID; + + return $this; + } + + /** + * @return string + */ + public function getEntityID() + { + return $this->entityID; + } + + /** + * @param string|null $id + * + * @return EntityDescriptor + */ + public function setID($id) + { + $this->id = null !== $id ? (string) $id : null; + + return $this; + } + + /** + * @return string|null + */ + public function getID() + { + return $this->id; + } + + /** + * @param \LightSaml\Model\Metadata\IdpSsoDescriptor|\LightSaml\Model\Metadata\SpSsoDescriptor $item + * + * @throws \InvalidArgumentException + * + * @return EntityDescriptor + */ + public function addItem($item) + { + if (false == $item instanceof IdpSsoDescriptor && + false == $item instanceof SpSsoDescriptor + ) { + throw new \InvalidArgumentException('EntityDescriptor item must be IdpSsoDescriptor or SpSsoDescriptor'); + } + + if (false == is_array($this->items)) { + $this->items = array(); + } + + $this->items[] = $item; + + return $this; + } + + /** + * @return IdpSsoDescriptor[]|SpSsoDescriptor[]|SSODescriptor[] + */ + public function getAllItems() + { + return $this->items; + } + + /** + * @return IdpSsoDescriptor[] + */ + public function getAllIdpSsoDescriptors() + { + $result = array(); + foreach ($this->getAllItems() as $item) { + if ($item instanceof IdpSsoDescriptor) { + $result[] = $item; + } + } + + return $result; + } + + /** + * @return SpSsoDescriptor[] + */ + public function getAllSpSsoDescriptors() + { + $result = array(); + foreach ($this->getAllItems() as $item) { + if ($item instanceof SpSsoDescriptor) { + $result[] = $item; + } + } + + return $result; + } + + /** + * @return IdpSsoDescriptor|null + */ + public function getFirstIdpSsoDescriptor() + { + foreach ($this->getAllItems() as $item) { + if ($item instanceof IdpSsoDescriptor) { + return $item; + } + } + + return null; + } + + /** + * @return SpSsoDescriptor|null + */ + public function getFirstSpSsoDescriptor() + { + foreach ($this->getAllItems() as $item) { + if ($item instanceof SpSsoDescriptor) { + return $item; + } + } + + return null; + } + + /** + * @param Signature|null $signature + * + * @return EntityDescriptor + */ + public function setSignature(Signature $signature) + { + $this->signature = $signature; + + return $this; + } + + /** + * @return Signature|null + */ + public function getSignature() + { + return $this->signature; + } + + /** + * @param int $validUntil + * + * @return EntityDescriptor + */ + public function setValidUntil($validUntil) + { + $this->validUntil = Helper::getTimestampFromValue($validUntil); + + return $this; + } + + /** + * @return int|null + */ + public function getValidUntilTimestamp() + { + return $this->validUntil; + } + + /** + * @return string|null + */ + public function getValidUntilString() + { + if ($this->validUntil) { + return Helper::time2string($this->validUntil); + } + + return null; + } + + /** + * @return \DateTime|null + */ + public function getValidUntilDateTime() + { + if ($this->validUntil) { + return new \DateTime('@'.$this->validUntil); + } + + return null; + } + + /** + * @return array|KeyDescriptor[] + */ + public function getAllIdpKeyDescriptors() + { + $result = array(); + foreach ($this->getAllIdpSsoDescriptors() as $idp) { + foreach ($idp->getAllKeyDescriptors() as $key) { + $result[] = $key; + } + } + + return $result; + } + + /** + * @return array|KeyDescriptor[] + */ + public function getAllSpKeyDescriptors() + { + $result = array(); + foreach ($this->getAllSpSsoDescriptors() as $sp) { + foreach ($sp->getAllKeyDescriptors() as $key) { + $result[] = $key; + } + } + + return $result; + } + + /** + * @return EndpointReference[] + */ + public function getAllEndpoints() + { + $result = array(); + foreach ($this->getAllIdpSsoDescriptors() as $idpSsoDescriptor) { + foreach ($idpSsoDescriptor->getAllSingleSignOnServices() as $sso) { + $result[] = new EndpointReference($this, $idpSsoDescriptor, $sso); + } + foreach ($idpSsoDescriptor->getAllSingleLogoutServices() as $slo) { + $result[] = new EndpointReference($this, $idpSsoDescriptor, $slo); + } + } + foreach ($this->getAllSpSsoDescriptors() as $spSsoDescriptor) { + foreach ($spSsoDescriptor->getAllAssertionConsumerServices() as $acs) { + $result[] = new EndpointReference($this, $spSsoDescriptor, $acs); + } + foreach ($spSsoDescriptor->getAllSingleLogoutServices() as $slo) { + $result[] = new EndpointReference($this, $spSsoDescriptor, $slo); + } + } + + return $result; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('EntityDescriptor', SamlConstants::NS_METADATA, $parent, $context); + + $this->attributesToXml(array('entityID', 'validUntil', 'cacheDuration', 'ID'), $result); + + $this->manyElementsToXml($this->getAllItems(), $result, $context, null); + if ($this->organizations) { + $this->manyElementsToXml($this->organizations, $result, $context, null); + } + if ($this->contactPersons) { + $this->manyElementsToXml($this->contactPersons, $result, $context, null); + } + + $this->singleElementsToXml(array('Signature'), $result, $context); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'EntityDescriptor', SamlConstants::NS_METADATA); + + $this->attributesFromXml($node, array('entityID', 'validUntil', 'cacheDuration', 'ID')); + + $this->items = array(); + + $this->manyElementsFromXml( + $node, + $context, + 'IDPSSODescriptor', + 'md', + 'LightSaml\Model\Metadata\IdpSsoDescriptor', + 'addItem' + ); + + $this->manyElementsFromXml( + $node, + $context, + 'SPSSODescriptor', + 'md', + 'LightSaml\Model\Metadata\SpSsoDescriptor', + 'addItem' + ); + + $this->manyElementsFromXml( + $node, + $context, + 'Organization', + 'md', + 'LightSaml\Model\Metadata\Organization', + 'addOrganization' + ); + + $this->manyElementsFromXml( + $node, + $context, + 'ContactPerson', + 'md', + 'LightSaml\Model\Metadata\ContactPerson', + 'addContactPerson' + ); + + $this->singleElementsFromXml($node, $context, array( + 'Signature' => array('ds', 'LightSaml\Model\XmlDSig\SignatureXmlReader'), + )); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/IdpSsoDescriptor.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/IdpSsoDescriptor.php new file mode 100644 index 0000000000..27d7c4e965 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/IdpSsoDescriptor.php @@ -0,0 +1,202 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Metadata; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\Assertion\Attribute; +use LightSaml\SamlConstants; + +class IdpSsoDescriptor extends SSODescriptor +{ + /** @var bool|null */ + protected $wantAuthnRequestsSigned; + + /** @var SingleSignOnService[]|null */ + protected $singleSignOnServices; + + /** @var Attribute[]|null */ + protected $attributes; + + /** + * @param bool|null $wantAuthnRequestsSigned + * + * @return IdpSsoDescriptor + */ + public function setWantAuthnRequestsSigned($wantAuthnRequestsSigned) + { + $this->wantAuthnRequestsSigned = filter_var($wantAuthnRequestsSigned, FILTER_VALIDATE_BOOLEAN, ['flags' => FILTER_NULL_ON_FAILURE]); + + return $this; + } + + /** + * @return bool|null + */ + public function getWantAuthnRequestsSigned() + { + return $this->wantAuthnRequestsSigned; + } + + /** + * @param SingleSignOnService $singleSignOnService + * + * @return IdpSsoDescriptor + */ + public function addSingleSignOnService(SingleSignOnService $singleSignOnService) + { + if (false == is_array($this->singleSignOnServices)) { + $this->singleSignOnServices = array(); + } + $this->singleSignOnServices[] = $singleSignOnService; + + return $this; + } + + /** + * @return SingleSignOnService[]|null + */ + public function getAllSingleSignOnServices() + { + return $this->singleSignOnServices; + } + + /** + * @param string $url + * + * @return SingleSignOnService[] + */ + public function getAllSingleSignOnServicesByUrl($url) + { + $result = array(); + foreach ($this->getAllSingleSignOnServices() as $svc) { + if ($svc->getLocation() == $url) { + $result[] = $svc; + } + } + + return $result; + } + + /** + * @param string $binding + * + * @return SingleSignOnService[] + */ + public function getAllSingleSignOnServicesByBinding($binding) + { + $result = array(); + foreach ($this->getAllSingleSignOnServices() as $svc) { + if ($svc->getBinding() == $binding) { + $result[] = $svc; + } + } + + return $result; + } + + /** + * @param string|null $binding + * + * @return SingleSignOnService|null + */ + public function getFirstSingleSignOnService($binding = null) + { + foreach ($this->getAllSingleSignOnServices() as $svc) { + if (null == $binding || $svc->getBinding() == $binding) { + return $svc; + } + } + + return null; + } + + /** + * @param \LightSaml\Model\Assertion\Attribute $attribute + * + * @return IdpSsoDescriptor + */ + public function addAttribute(Attribute $attribute) + { + if (false == is_array($this->attributes)) { + $this->attributes = array(); + } + $this->attributes[] = $attribute; + + return $this; + } + + /** + * @return \LightSaml\Model\Assertion\Attribute[]|null + */ + public function getAllAttributes() + { + return $this->attributes; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('IDPSSODescriptor', SamlConstants::NS_METADATA, $parent, $context); + + parent::serialize($result, $context); + + $this->attributesToXml(array('WantAuthnRequestsSigned'), $result); + + if ($this->getAllSingleSignOnServices()) { + foreach ($this->getAllSingleSignOnServices() as $object) { + $object->serialize($result, $context); + } + } + if ($this->getAllAttributes()) { + foreach ($this->getAllAttributes() as $object) { + $object->serialize($result, $context); + } + } + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'IDPSSODescriptor', SamlConstants::NS_METADATA); + + parent::deserialize($node, $context); + + $this->attributesFromXml($node, array('WantAuthnRequestsSigned')); + + $this->singleSignOnServices = array(); + $this->manyElementsFromXml( + $node, + $context, + 'SingleSignOnService', + 'md', + 'LightSaml\Model\Metadata\SingleSignOnService', + 'addSingleSignOnService' + ); + + $this->attributes = array(); + $this->manyElementsFromXml( + $node, + $context, + 'Attribute', + 'saml', + 'LightSaml\Model\Assertion\Attribute', + 'addAttribute' + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/IndexedEndpoint.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/IndexedEndpoint.php new file mode 100644 index 0000000000..f592117b8f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/IndexedEndpoint.php @@ -0,0 +1,93 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Metadata; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; + +class IndexedEndpoint extends Endpoint +{ + /** @var int */ + protected $index; + + /** @var bool|null */ + protected $isDefault; + + /** + * @param bool|null $isDefault + * + * @return IndexedEndpoint + */ + public function setIsDefault($isDefault) + { + $this->isDefault = filter_var($isDefault, FILTER_VALIDATE_BOOLEAN, ['flags' => FILTER_NULL_ON_FAILURE]); + + return $this; + } + + /** + * @return string|null + */ + public function getIsDefaultString() + { + return $this->isDefault ? 'true' : 'false'; + } + + /** + * @return bool|null + */ + public function getIsDefaultBool() + { + return $this->isDefault; + } + + /** + * @param int $index + * + * @return IndexedEndpoint + */ + public function setIndex($index) + { + $this->index = (int) $index; + + return $this; + } + + /** + * @return int + */ + public function getIndex() + { + return $this->index; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $this->attributesToXml(array('index', 'isDefault'), $parent); + parent::serialize($parent, $context); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->attributesFromXml($node, array('index', 'isDefault')); + + parent::deserialize($node, $context); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/KeyDescriptor.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/KeyDescriptor.php new file mode 100644 index 0000000000..5491f13073 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/KeyDescriptor.php @@ -0,0 +1,132 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Metadata; + +use LightSaml\Error\LightSamlXmlException; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\SamlConstants; +use LightSaml\Credential\X509Certificate; + +class KeyDescriptor extends AbstractSamlModel +{ + const USE_SIGNING = 'signing'; + const USE_ENCRYPTION = 'encryption'; + + /** @var string */ + protected $use; + + /** @var X509Certificate */ + private $certificate; + + /** + * @param string|null $use + * @param X509Certificate|null $certificate + */ + public function __construct($use = null, X509Certificate $certificate = null) + { + $this->use = $use; + $this->certificate = $certificate; + } + + /** + * @param string $use + * + * @return KeyDescriptor + * + * @throws \InvalidArgumentException + */ + public function setUse($use) + { + $use = trim($use); + if (false != $use && self::USE_ENCRYPTION != $use && self::USE_SIGNING != $use) { + throw new \InvalidArgumentException(sprintf("Invalid use value '%s'", $use)); + } + $this->use = $use; + + return $this; + } + + /** + * @return string + */ + public function getUse() + { + return $this->use; + } + + /** + * @param X509Certificate $certificate + * + * @return KeyDescriptor + */ + public function setCertificate(X509Certificate $certificate) + { + $this->certificate = $certificate; + + return $this; + } + + /** + * @return X509Certificate + */ + public function getCertificate() + { + return $this->certificate; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('KeyDescriptor', SamlConstants::NS_METADATA, $parent, $context); + + $this->attributesToXml(array('use'), $result); + + $keyInfo = $this->createElement('ds:KeyInfo', SamlConstants::NS_XMLDSIG, $result, $context); + $xData = $this->createElement('ds:X509Data', SamlConstants::NS_XMLDSIG, $keyInfo, $context); + $xCert = $this->createElement('ds:X509Certificate', SamlConstants::NS_XMLDSIG, $xData, $context); + + $xCert->nodeValue = $this->getCertificate()->getData(); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'KeyDescriptor', SamlConstants::NS_METADATA); + + $this->attributesFromXml($node, array('use')); + + $list = $context->getXpath()->query('./ds:KeyInfo/ds:X509Data/ds:X509Certificate', $node); + if (1 != $list->length) { + throw new LightSamlXmlException('Missing X509Certificate node'); + } + + /** @var $x509CertificateNode \DOMElement */ + $x509CertificateNode = $list->item(0); + $certificateData = trim($x509CertificateNode->textContent); + if (false == $certificateData) { + throw new LightSamlXmlException('Missing certificate data'); + } + + $this->certificate = new X509Certificate(); + $this->certificate->setData($certificateData); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/Metadata.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/Metadata.php new file mode 100644 index 0000000000..d60fb1419e --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/Metadata.php @@ -0,0 +1,89 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Metadata; + +use LightSaml\Error\LightSamlXmlException; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\SamlElementInterface; +use LightSaml\SamlConstants; + +abstract class Metadata extends AbstractSamlModel +{ + /** + * @param string $path + * + * @return EntitiesDescriptor|EntityDescriptor + */ + public static function fromFile($path) + { + $deserializatonContext = new DeserializationContext(); + $xml = file_get_contents($path); + + return self::fromXML($xml, $deserializatonContext); + } + + /** + * @param string $xml + * @param DeserializationContext $context + * + * @return EntityDescriptor|EntitiesDescriptor + * + * @throws \Exception + */ + public static function fromXML($xml, DeserializationContext $context) + { + if (false == is_string($xml)) { + throw new \InvalidArgumentException('Expecting string'); + } + + $context->getDocument()->loadXML($xml); + + $node = $context->getDocument()->firstChild; + while ($node && $node instanceof \DOMComment) { + $node = $node->nextSibling; + } + if (null === $node) { + throw new LightSamlXmlException('Empty XML'); + } + + if (SamlConstants::NS_METADATA !== $node->namespaceURI) { + throw new LightSamlXmlException(sprintf( + "Invalid namespace '%s' of the root XML element, expected '%s'", + $node->namespaceURI, + SamlConstants::NS_METADATA + )); + } + + $map = array( + 'EntityDescriptor' => '\LightSaml\Model\Metadata\EntityDescriptor', + 'EntitiesDescriptor' => '\LightSaml\Model\Metadata\EntitiesDescriptor', + ); + + $rootElementName = $node->localName; + + if (array_key_exists($rootElementName, $map)) { + if ($class = $map[$rootElementName]) { + /** @var SamlElementInterface $result */ + $result = new $class(); + } else { + throw new \LogicException('Deserialization of %s root element is not implemented'); + } + } else { + throw new LightSamlXmlException(sprintf("Unknown SAML metadata '%s'", $rootElementName)); + } + + $result->deserialize($node, $context); + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/Organization.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/Organization.php new file mode 100644 index 0000000000..1c377ab7c2 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/Organization.php @@ -0,0 +1,159 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Metadata; + +use LightSaml\Error\LightSamlXmlException; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\SamlConstants; + +class Organization extends AbstractSamlModel +{ + /** @var string */ + protected $organizationName; + + /** @var string */ + protected $organizationDisplayName; + + /** @var string */ + protected $organizationURL; + + protected $lang = 'en-US'; + + /** + * @return string + */ + public function getLang() + { + return $this->lang; + } + + /** + * @param string $lang + * + * @return Organization + */ + public function setLang($lang) + { + $this->lang = $lang; + + return $this; + } + + /** + * @param string $organizationDisplayName + * + * @return Organization + */ + public function setOrganizationDisplayName($organizationDisplayName) + { + $this->organizationDisplayName = (string) $organizationDisplayName; + + return $this; + } + + /** + * @return string + */ + public function getOrganizationDisplayName() + { + return $this->organizationDisplayName; + } + + /** + * @param string $organizationName + * + * @return Organization + */ + public function setOrganizationName($organizationName) + { + $this->organizationName = (string) $organizationName; + + return $this; + } + + /** + * @return string + */ + public function getOrganizationName() + { + return $this->organizationName; + } + + /** + * @param string $organizationURL + * + * @return Organization + */ + public function setOrganizationURL($organizationURL) + { + $this->organizationURL = (string) $organizationURL; + + return $this; + } + + /** + * @return string + */ + public function getOrganizationURL() + { + return $this->organizationURL; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + if (!$this->lang) { + throw new LightSamlXmlException('Lang is required'); + } + + $result = $this->createElement('Organization', SamlConstants::NS_METADATA, $parent, $context); + + $elements = array('OrganizationName', 'OrganizationDisplayName', 'OrganizationURL'); + $this->singleElementsToXml( + $elements, + $result, + $context, + SamlConstants::NS_METADATA + ); + + /** @var \DOMNode $node */ + foreach ($result->childNodes as $node) { + if ($node instanceof \DOMElement) { + if (in_array($node->tagName, $elements)) { + $node->setAttribute('xml:lang', $this->lang); + } + } + } + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'Organization', SamlConstants::NS_METADATA); + + $this->singleElementsFromXml($node, $context, array( + 'OrganizationName' => array('md', null), + 'OrganizationDisplayName' => array('md', null), + 'OrganizationURL' => array('md', null), + )); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/RoleDescriptor.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/RoleDescriptor.php new file mode 100644 index 0000000000..0fd6c8bae9 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/RoleDescriptor.php @@ -0,0 +1,368 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Metadata; + +use LightSaml\Helper; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\Model\XmlDSig\Signature; +use LightSaml\SamlConstants; + +abstract class RoleDescriptor extends AbstractSamlModel +{ + /** @var string|null */ + protected $id; + + /** @var int|null */ + protected $validUntil; + + /** @var string|null */ + protected $cacheDuration; + + /** @var string */ + protected $protocolSupportEnumeration = SamlConstants::PROTOCOL_SAML2; + + /** @var string|null */ + protected $errorURL; + + /** @var Signature[]|null */ + protected $signatures; + + /** @var KeyDescriptor[]|null */ + protected $keyDescriptors; + + /** @var Organization[]|null */ + protected $organizations; + + /** @var ContactPerson[]|null */ + protected $contactPersons; + + /** + * @param null|string $cacheDuration + * + * @throws \InvalidArgumentException + * + * @return RoleDescriptor + */ + public function setCacheDuration($cacheDuration) + { + Helper::validateDurationString($cacheDuration); + + $this->cacheDuration = $cacheDuration; + + return $this; + } + + /** + * @return null|string + */ + public function getCacheDuration() + { + return $this->cacheDuration; + } + + /** + * @param ContactPerson $contactPerson + * + * @return RoleDescriptor + */ + public function addContactPerson(ContactPerson $contactPerson) + { + if (false == is_array($this->contactPersons)) { + $this->contactPersons = array(); + } + $this->contactPersons[] = $contactPerson; + + return $this; + } + + /** + * @return \LightSaml\Model\Metadata\ContactPerson[]|null + */ + public function getAllContactPersons() + { + return $this->contactPersons; + } + + /** + * @param null|string $errorURL + * + * @return RoleDescriptor + */ + public function setErrorURL($errorURL) + { + $this->errorURL = (string) $errorURL; + + return $this; + } + + /** + * @return null|string + */ + public function getErrorURL() + { + return $this->errorURL; + } + + /** + * @param null|string $id + * + * @return RoleDescriptor + */ + public function setID($id) + { + $this->id = (string) $id; + + return $this; + } + + /** + * @return null|string + */ + public function getID() + { + return $this->id; + } + + /** + * @param KeyDescriptor $keyDescriptor + * + * @return RoleDescriptor + */ + public function addKeyDescriptor(KeyDescriptor $keyDescriptor) + { + if (false == is_array($this->keyDescriptors)) { + $this->keyDescriptors = array(); + } + $this->keyDescriptors[] = $keyDescriptor; + + return $this; + } + + /** + * @return \LightSaml\Model\Metadata\KeyDescriptor[]|null + */ + public function getAllKeyDescriptors() + { + return $this->keyDescriptors; + } + + /** + * @param string $use + * + * @return KeyDescriptor[] + */ + public function getAllKeyDescriptorsByUse($use) + { + $result = array(); + foreach ($this->getAllKeyDescriptors() as $kd) { + if ($kd->getUse() == $use) { + $result[] = $kd; + } + } + + return $result; + } + + /** + * @param string|null $use + * + * @return KeyDescriptor|null + */ + public function getFirstKeyDescriptor($use = null) + { + if ($this->getAllKeyDescriptors()) { + foreach ($this->getAllKeyDescriptors() as $kd) { + if (null == $use || $kd->getUse() == $use) { + return $kd; + } + } + } + + return; + } + + /** + * @param Organization $organization + * + * @return RoleDescriptor + */ + public function addOrganization(Organization $organization) + { + if (false == is_array($this->organizations)) { + $this->organizations = array(); + } + $this->organizations[] = $organization; + + return $this; + } + + /** + * @return Organization[]|null + */ + public function getAllOrganizations() + { + return $this->organizations; + } + + /** + * @param string $protocolSupportEnumeration + * + * @return RoleDescriptor + */ + public function setProtocolSupportEnumeration($protocolSupportEnumeration) + { + $this->protocolSupportEnumeration = (string) $protocolSupportEnumeration; + + return $this; + } + + /** + * @return string + */ + public function getProtocolSupportEnumeration() + { + return $this->protocolSupportEnumeration; + } + + /** + * @param \LightSaml\Model\XmlDSig\Signature $signature + * + * @return RoleDescriptor + */ + public function addSignature(Signature $signature) + { + if (false == is_array($this->signatures)) { + $this->signatures = array(); + } + $this->signatures[] = $signature; + + return $this; + } + + /** + * @return \LightSaml\Model\XmlDSig\Signature[]|null + */ + public function getAllSignatures() + { + return $this->signatures; + } + + /** + * @param int|null $validUntil + * + * @return RoleDescriptor + */ + public function setValidUntil($validUntil) + { + $this->validUntil = Helper::getTimestampFromValue($validUntil); + + return $this; + } + + /** + * @return string + */ + public function getValidUntilString() + { + if ($this->validUntil) { + return Helper::time2string($this->validUntil); + } + + return; + } + + /** + * @return int + */ + public function getValidUntilTimestamp() + { + return $this->validUntil; + } + + /** + * @return \DateTime|null + */ + public function getValidUntilDateTime() + { + if ($this->validUntil) { + return new \DateTime('@'.$this->validUntil); + } + + return null; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $this->attributesToXml( + array('protocolSupportEnumeration', 'ID', 'validUntil', 'cacheDuration', 'errorURL'), + $parent + ); + + $this->manyElementsToXml($this->getAllSignatures(), $parent, $context, null); + $this->manyElementsToXml($this->getAllKeyDescriptors(), $parent, $context, null); + $this->manyElementsToXml($this->getAllOrganizations(), $parent, $context, null); + $this->manyElementsToXml($this->getAllContactPersons(), $parent, $context, null); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->attributesFromXml( + $node, + array('protocolSupportEnumeration', 'ID', 'validUntil', 'cacheDuration', 'errorURL') + ); + + $this->manyElementsFromXml( + $node, + $context, + 'Signature', + 'ds', + 'LightSaml\Model\XmlDSig\Signature', + 'addSignature' + ); + $this->manyElementsFromXml( + $node, + $context, + 'KeyDescriptor', + 'md', + 'LightSaml\Model\Metadata\KeyDescriptor', + 'addKeyDescriptor' + ); + $this->manyElementsFromXml( + $node, + $context, + 'Organization', + 'md', + 'LightSaml\Model\Metadata\Organization', + 'addOrganization' + ); + $this->manyElementsFromXml( + $node, + $context, + 'ContactPerson', + 'md', + 'LightSaml\Model\Metadata\ContactPerson', + 'addContactPerson' + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/SSODescriptor.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/SSODescriptor.php new file mode 100644 index 0000000000..da9715cd39 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/SSODescriptor.php @@ -0,0 +1,148 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Metadata; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\SamlConstants; + +abstract class SSODescriptor extends RoleDescriptor +{ + /** @var SingleLogoutService[] */ + protected $singleLogoutServices = array(); + + /** @var string[]|null */ + protected $nameIDFormats; + + /** + * @param SingleLogoutService $singleLogoutService + * + * @return SSODescriptor + */ + public function addSingleLogoutService(SingleLogoutService $singleLogoutService) + { + $this->singleLogoutServices[] = $singleLogoutService; + + return $this; + } + + /** + * @return SingleLogoutService[] + */ + public function getAllSingleLogoutServices() + { + return $this->singleLogoutServices; + } + + /** + * @param string $binding + * + * @return SingleLogoutService[] + */ + public function getAllSingleLogoutServicesByBinding($binding) + { + $result = array(); + foreach ($this->getAllSingleLogoutServices() as $svc) { + if ($binding == $svc->getBinding()) { + $result[] = $svc; + } + } + + return $result; + } + + /** + * @param string|null $binding + * + * @return SingleLogoutService|null + */ + public function getFirstSingleLogoutService($binding = null) + { + foreach ($this->getAllSingleLogoutServices() as $svc) { + if (null == $binding || $binding == $svc->getBinding()) { + return $svc; + } + } + + return null; + } + + /** + * @param string $nameIDFormat + * + * @return SSODescriptor + */ + public function addNameIDFormat($nameIDFormat) + { + $this->nameIDFormats[] = $nameIDFormat; + + return $this; + } + + /** + * @return null|string[] + */ + public function getAllNameIDFormats() + { + return $this->nameIDFormats; + } + + /** + * @param string $nameIdFormat + * + * @return bool + */ + public function hasNameIDFormat($nameIdFormat) + { + if ($this->nameIDFormats) { + foreach ($this->nameIDFormats as $format) { + if ($format == $nameIdFormat) { + return true; + } + } + } + + return false; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + parent::serialize($parent, $context); + + $this->manyElementsToXml($this->getAllSingleLogoutServices(), $parent, $context, null); + $this->manyElementsToXml($this->getAllNameIDFormats(), $parent, $context, 'NameIDFormat', SamlConstants::NS_METADATA); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + parent::deserialize($node, $context); + + $this->manyElementsFromXml($node, $context, 'NameIDFormat', 'md', null, 'addNameIDFormat'); + + $this->manyElementsFromXml( + $node, + $context, + 'SingleLogoutService', + 'md', + 'LightSaml\Model\Metadata\SingleLogoutService', + 'addSingleLogoutService' + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/SingleLogoutService.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/SingleLogoutService.php new file mode 100644 index 0000000000..7132de9e96 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/SingleLogoutService.php @@ -0,0 +1,36 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Metadata; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\SamlConstants; + +class SingleLogoutService extends Endpoint +{ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('SingleLogoutService', SamlConstants::NS_METADATA, $parent, $context); + parent::serialize($result, $context); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'SingleLogoutService', SamlConstants::NS_METADATA); + + parent::deserialize($node, $context); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/SingleSignOnService.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/SingleSignOnService.php new file mode 100644 index 0000000000..55a1ea418c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/SingleSignOnService.php @@ -0,0 +1,33 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Metadata; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\SamlConstants; + +class SingleSignOnService extends Endpoint +{ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('SingleSignOnService', SamlConstants::NS_METADATA, $parent, $context); + + parent::serialize($result, $context); + } + + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'SingleSignOnService', SamlConstants::NS_METADATA); + + parent::deserialize($node, $context); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/SpSsoDescriptor.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/SpSsoDescriptor.php new file mode 100644 index 0000000000..eb87aa1db7 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Metadata/SpSsoDescriptor.php @@ -0,0 +1,197 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Metadata; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\SamlConstants; + +class SpSsoDescriptor extends SSODescriptor +{ + /** @var bool|null */ + protected $authnRequestsSigned; + + /** @var bool|null */ + protected $wantAssertionsSigned; + + /** @var AssertionConsumerService[]|null */ + protected $assertionConsumerServices; + + /** + * @param AssertionConsumerService $assertionConsumerService + * + * @return SpSsoDescriptor + */ + public function addAssertionConsumerService(AssertionConsumerService $assertionConsumerService) + { + if (false == is_array($this->assertionConsumerServices)) { + $this->assertionConsumerServices = array(); + } + if (null === $assertionConsumerService->getIndex()) { + $assertionConsumerService->setIndex(count($this->assertionConsumerServices)); + } + $this->assertionConsumerServices[] = $assertionConsumerService; + + return $this; + } + + /** + * @return AssertionConsumerService[]|null + */ + public function getAllAssertionConsumerServices() + { + return $this->assertionConsumerServices; + } + + /** + * @param string $binding + * + * @return AssertionConsumerService[] + */ + public function getAllAssertionConsumerServicesByBinding($binding) + { + $result = array(); + foreach ($this->getAllAssertionConsumerServices() as $svc) { + if ($svc->getBinding() == $binding) { + $result[] = $svc; + } + } + + return $result; + } + + /** + * @param string $url + * + * @return AssertionConsumerService[] + */ + public function getAllAssertionConsumerServicesByUrl($url) + { + $result = array(); + foreach ($this->getAllAssertionConsumerServices() as $svc) { + if ($svc->getLocation() == $url) { + $result[] = $svc; + } + } + + return $result; + } + + /** + * @param int $index + * + * @return AssertionConsumerService|null + */ + public function getAssertionConsumerServicesByIndex($index) + { + foreach ($this->getAllAssertionConsumerServices() as $svc) { + if ($svc->getIndex() == $index) { + return $svc; + } + } + + return null; + } + + /** + * @param string|null $binding + * + * @return AssertionConsumerService|null + */ + public function getFirstAssertionConsumerService($binding = null) + { + foreach ($this->getAllAssertionConsumerServices() as $svc) { + if (null == $binding || $svc->getBinding() == $binding) { + return $svc; + } + } + + return null; + } + + /** + * @param bool|null $authnRequestsSigned + * + * @return SpSsoDescriptor + */ + public function setAuthnRequestsSigned($authnRequestsSigned) + { + $this->authnRequestsSigned = filter_var($authnRequestsSigned, FILTER_VALIDATE_BOOLEAN, ['flags' => FILTER_NULL_ON_FAILURE]); + + return $this; + } + + /** + * @return bool|null + */ + public function getAuthnRequestsSigned() + { + return $this->authnRequestsSigned; + } + + /** + * @param bool|null $wantAssertionsSigned + * + * @return SpSsoDescriptor + */ + public function setWantAssertionsSigned($wantAssertionsSigned) + { + $this->wantAssertionsSigned = filter_var($wantAssertionsSigned, FILTER_VALIDATE_BOOLEAN, ['flags' => FILTER_NULL_ON_FAILURE]); + + return $this; + } + + /** + * @return bool|null + */ + public function getWantAssertionsSigned() + { + return $this->wantAssertionsSigned; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('SPSSODescriptor', SamlConstants::NS_METADATA, $parent, $context); + + parent::serialize($result, $context); + + $this->attributesToXml(array('AuthnRequestsSigned', 'WantAssertionsSigned'), $result); + + $this->manyElementsToXml($this->getAllAssertionConsumerServices(), $result, $context, null); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'SPSSODescriptor', SamlConstants::NS_METADATA); + + parent::deserialize($node, $context); + + $this->attributesFromXml($node, array('AuthnRequestsSigned', 'WantAssertionsSigned')); + + $this->manyElementsFromXml( + $node, + $context, + 'AssertionConsumerService', + 'md', + 'LightSaml\Model\Metadata\AssertionConsumerService', + 'addAssertionConsumerService' + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/AbstractRequest.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/AbstractRequest.php new file mode 100644 index 0000000000..e124bef07f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/AbstractRequest.php @@ -0,0 +1,16 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Protocol; + +abstract class AbstractRequest extends SamlMessage +{ +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/AuthnRequest.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/AuthnRequest.php new file mode 100644 index 0000000000..722c17836a --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/AuthnRequest.php @@ -0,0 +1,332 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Protocol; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\Assertion\Conditions; +use LightSaml\Model\Assertion\Subject; +use LightSaml\SamlConstants; + +class AuthnRequest extends AbstractRequest +{ + //region Attributes + + /** @var bool|null */ + protected $forceAuthn; + + /** @var bool|null */ + protected $isPassive; + + /** @var int|null */ + protected $assertionConsumerServiceIndex; + + /** @var string|null */ + protected $assertionConsumerServiceURL; + + /** @var int|null */ + protected $attributeConsumingServiceIndex; + + /** @var string|null */ + protected $protocolBinding; + + /** @var string|null */ + protected $providerName; + + //endregion + + //region Elements + + /** @var Conditions|null */ + protected $conditions; + + /** @var NameIDPolicy|null */ + protected $nameIDPolicy; + + /** @var Subject|null */ + protected $subject; + + /** + * @param Subject|null $subject + * + * @return AuthnRequest + */ + public function setSubject(Subject $subject) + { + $this->subject = $subject; + + return $this; + } + + /** + * @return Subject|null + */ + public function getSubject() + { + return $this->subject; + } + + /** + * @param null|string $providerName + * + * @return AuthnRequest + */ + public function setProviderName($providerName) + { + $this->providerName = (string) $providerName; + + return $this; + } + + /** + * @return null|string + */ + public function getProviderName() + { + return $this->providerName; + } + + /** + * @param null|string $protocolBinding + * + * @return AuthnRequest + */ + public function setProtocolBinding($protocolBinding) + { + $this->protocolBinding = (string) $protocolBinding; + + return $this; + } + + /** + * @return null|string + */ + public function getProtocolBinding() + { + return $this->protocolBinding; + } + + /** + * @param NameIDPolicy|null $nameIDPolicy + * + * @return AuthnRequest + */ + public function setNameIDPolicy(NameIDPolicy $nameIDPolicy) + { + $this->nameIDPolicy = $nameIDPolicy; + + return $this; + } + + /** + * @return NameIDPolicy|null + */ + public function getNameIDPolicy() + { + return $this->nameIDPolicy; + } + + /** + * @param bool|null $isPassive + * + * @return AuthnRequest + */ + public function setIsPassive($isPassive) + { + $this->isPassive = 0 == strcasecmp($isPassive, 'true') || true === $isPassive || 1 == $isPassive; + + return $this; + } + + /** + * @return bool|null + */ + public function getIsPassive() + { + return $this->isPassive; + } + + /** + * @return string|null + */ + public function getIsPassiveString() + { + if (null === $this->isPassive) { + return null; + } + + return $this->isPassive ? 'true' : 'false'; + } + + /** + * @param bool|null $forceAuthn + * + * @return AuthnRequest + */ + public function setForceAuthn($forceAuthn) + { + $this->forceAuthn = 0 == strcasecmp($forceAuthn, 'true') || true === $forceAuthn || 1 == $forceAuthn; + + return $this; + } + + /** + * @return bool|null + */ + public function getForceAuthn() + { + return $this->forceAuthn; + } + + /** + * @return string|null + */ + public function getForceAuthnString() + { + if (null === $this->forceAuthn) { + return null; + } + + return $this->forceAuthn ? 'true' : 'false'; + } + + /** + * @param Conditions|null $conditions + * + * @return AuthnRequest + */ + public function setConditions($conditions) + { + $this->conditions = $conditions; + + return $this; + } + + /** + * @return \LightSaml\Model\Assertion\Conditions|null + */ + public function getConditions() + { + return $this->conditions; + } + + /** + * @param null|int $attributeConsumingServiceIndex + * + * @return AuthnRequest + */ + public function setAttributeConsumingServiceIndex($attributeConsumingServiceIndex) + { + $this->attributeConsumingServiceIndex = null !== $attributeConsumingServiceIndex + ? intval(((string) $attributeConsumingServiceIndex)) + : null; + + return $this; + } + + /** + * @return null|int + */ + public function getAttributeConsumingServiceIndex() + { + return $this->attributeConsumingServiceIndex; + } + + /** + * @param null|string $assertionConsumerServiceURL + * + * @return AuthnRequest + */ + public function setAssertionConsumerServiceURL($assertionConsumerServiceURL) + { + $this->assertionConsumerServiceURL = (string) $assertionConsumerServiceURL; + + return $this; + } + + /** + * @return null|string + */ + public function getAssertionConsumerServiceURL() + { + return $this->assertionConsumerServiceURL; + } + + /** + * @param null|int $assertionConsumerServiceIndex + * + * @return AuthnRequest + */ + public function setAssertionConsumerServiceIndex($assertionConsumerServiceIndex) + { + $this->assertionConsumerServiceIndex = null !== $assertionConsumerServiceIndex + ? intval((string) $assertionConsumerServiceIndex) + : null; + + return $this; + } + + /** + * @return null|int + */ + public function getAssertionConsumerServiceIndex() + { + return $this->assertionConsumerServiceIndex; + } + + //endregion + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('AuthnRequest', SamlConstants::NS_PROTOCOL, $parent, $context); + + parent::serialize($result, $context); + + $this->attributesToXml(array( + 'ForceAuthn', 'IsPassive', 'ProtocolBinding', 'AssertionConsumerServiceIndex', + 'AssertionConsumerServiceURL', 'AttributeConsumingServiceIndex', 'ProviderName', + ), $result); + + $this->singleElementsToXml(array('Subject', 'NameIDPolicy', 'Conditions'), $result, $context); + + // must be last in order signature to include them all + $this->singleElementsToXml(array('Signature'), $result, $context); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'AuthnRequest', SamlConstants::NS_PROTOCOL); + + parent::deserialize($node, $context); + + $this->attributesFromXml($node, array( + 'ForceAuthn', 'IsPassive', 'ProtocolBinding', 'AssertionConsumerServiceIndex', + 'AssertionConsumerServiceURL', 'AttributeConsumingServiceIndex', 'ProviderName', + )); + + $this->singleElementsFromXml($node, $context, array( + 'Subject' => array('saml', 'LightSaml\Model\Assertion\Subject'), + 'NameIDPolicy' => array('samlp', 'LightSaml\Model\Protocol\NameIDPolicy'), + 'Conditions' => array('saml', 'LightSaml\Model\Assertion\Conditions'), + )); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/LogoutRequest.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/LogoutRequest.php new file mode 100644 index 0000000000..4d850eb393 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/LogoutRequest.php @@ -0,0 +1,173 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Protocol; + +use LightSaml\Helper; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\Assertion\NameID; +use LightSaml\SamlConstants; + +class LogoutRequest extends AbstractRequest +{ + /** @var string|null */ + protected $reason; + + /** @var int|null */ + protected $notOnOrAfter; + + /** @var NameID */ + protected $nameID; + + /** @var string|null */ + protected $sessionIndex; + + /** + * @param NameID $nameID + * + * @return LogoutRequest + */ + public function setNameID(NameID $nameID) + { + $this->nameID = $nameID; + + return $this; + } + + /** + * @return NameID + */ + public function getNameID() + { + return $this->nameID; + } + + /** + * @param int|\DateTime|string $notOnOrAfter + * + * @return LogoutRequest + */ + public function setNotOnOrAfter($notOnOrAfter) + { + $this->notOnOrAfter = Helper::getTimestampFromValue($notOnOrAfter); + + return $this; + } + + /** + * @return int|null + */ + public function getNotOnOrAfterTimestamp() + { + return $this->notOnOrAfter; + } + + /** + * @return string|null + */ + public function getNotOnOrAfterString() + { + if ($this->notOnOrAfter) { + return Helper::time2string($this->notOnOrAfter); + } + + return null; + } + + /** + * @return \DateTime|null + */ + public function getNotOnOrAfterDateTime() + { + if ($this->notOnOrAfter) { + return new \DateTime('@'.$this->notOnOrAfter); + } + + return null; + } + + /** + * @param null|string $reason + * + * @return LogoutRequest + */ + public function setReason($reason) + { + $this->reason = (string) $reason; + + return $this; + } + + /** + * @return null|string + */ + public function getReason() + { + return $this->reason; + } + + /** + * @param null|string $sessionIndex + * + * @return LogoutRequest + */ + public function setSessionIndex($sessionIndex) + { + $this->sessionIndex = (string) $sessionIndex; + + return $this; + } + + /** + * @return null|string + */ + public function getSessionIndex() + { + return $this->sessionIndex; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('LogoutRequest', SamlConstants::NS_PROTOCOL, $parent, $context); + + parent::serialize($result, $context); + + $this->attributesToXml(array('Reason', 'NotOnOrAfter'), $result); + + $this->singleElementsToXml(array('NameID', 'SessionIndex'), $result, $context, SamlConstants::NS_PROTOCOL); + + // must be last in order signature to include them all + $this->singleElementsToXml(array('Signature'), $result, $context); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'LogoutRequest', SamlConstants::NS_PROTOCOL); + + parent::deserialize($node, $context); + + $this->attributesFromXml($node, array('Reason', 'NotOnOrAfter')); + + $this->singleElementsFromXml($node, $context, array( + 'NameID' => array('saml', 'LightSaml\Model\Assertion\NameID'), + 'SessionIndex' => array('samlp', null), + )); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/LogoutResponse.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/LogoutResponse.php new file mode 100644 index 0000000000..c029dcef99 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/LogoutResponse.php @@ -0,0 +1,44 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Protocol; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\SamlConstants; + +class LogoutResponse extends StatusResponse +{ + /** + * @param \DOMNode $parent + * @param SerializationContext $context + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('samlp:LogoutResponse', SamlConstants::NS_PROTOCOL, $parent, $context); + + parent::serialize($result, $context); + + // must be done here at the end and not in a base class where declared in order to include signing of the elements added here + $this->singleElementsToXml(array('Signature'), $result, $context); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'LogoutResponse', SamlConstants::NS_PROTOCOL); + + parent::deserialize($node, $context); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/NameIDPolicy.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/NameIDPolicy.php new file mode 100644 index 0000000000..cf87b92a3d --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/NameIDPolicy.php @@ -0,0 +1,147 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Protocol; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\SamlConstants; + +class NameIDPolicy extends AbstractSamlModel +{ + /** + * @var string|null + */ + protected $format; + + /** + * @var bool|null + */ + protected $allowCreate; + + /** + * @var string|null + */ + protected $spNameQualifier; + + /** + * @param string $format + * @param bool $allowCreate + */ + public function __construct($format = null, $allowCreate = null) + { + $this->allowCreate = $allowCreate; + $this->format = $format; + } + + /** + * @param string|bool|null $allowCreate + * + * @return NameIDPolicy + */ + public function setAllowCreate($allowCreate) + { + if (null === $allowCreate) { + $this->allowCreate = null; + } elseif (is_string($allowCreate) || is_int($allowCreate)) { + $this->allowCreate = 0 == strcasecmp($allowCreate, 'true') || true === $allowCreate || 1 == $allowCreate; + } else { + $this->allowCreate = (bool) $allowCreate; + } + + return $this; + } + + /** + * @return bool|null + */ + public function getAllowCreate() + { + return $this->allowCreate; + } + + /** + * @return string|null + */ + public function getAllowCreateString() + { + if (null === $this->allowCreate) { + return null; + } + + return $this->allowCreate ? 'true' : 'false'; + } + + /** + * @param string|null $format + * + * @return NameIDPolicy + */ + public function setFormat($format) + { + $this->format = (string) $format; + + return $this; + } + + /** + * @return string|null + */ + public function getFormat() + { + return $this->format; + } + + /** + * @param string|null $spNameQualifier + * + * @return NameIDPolicy + */ + public function setSPNameQualifier($spNameQualifier) + { + $this->spNameQualifier = $spNameQualifier; + + return $this; + } + + /** + * @return string|null + */ + public function getSPNameQualifier() + { + return $this->spNameQualifier; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('NameIDPolicy', SamlConstants::NS_PROTOCOL, $parent, $context); + + $this->attributesToXml(array('Format', 'SPNameQualifier', 'AllowCreate'), $result); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'NameIDPolicy', SamlConstants::NS_PROTOCOL); + + $this->attributesFromXml($node, array('Format', 'SPNameQualifier', 'AllowCreate')); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/Response.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/Response.php new file mode 100644 index 0000000000..1c9b553344 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/Response.php @@ -0,0 +1,183 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Protocol; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\Assertion\Assertion; +use LightSaml\Model\Assertion\EncryptedElement; +use LightSaml\SamlConstants; + +class Response extends StatusResponse +{ + /** @var Assertion[] */ + protected $assertions = array(); + + /** @var EncryptedElement[] */ + protected $encryptedAssertions = array(); + + /** + * @return Assertion[] + */ + public function getAllAssertions() + { + return $this->assertions; + } + + /** + * @return Assertion|null + */ + public function getFirstAssertion() + { + if (is_array($this->assertions) && isset($this->assertions[0])) { + return $this->assertions[0]; + } + + return null; + } + + /** + * @return EncryptedElement[] + */ + public function getAllEncryptedAssertions() + { + return $this->encryptedAssertions; + } + + /** + * @return EncryptedElement|null + */ + public function getFirstEncryptedAssertion() + { + if (is_array($this->encryptedAssertions) && isset($this->encryptedAssertions[0])) { + return $this->encryptedAssertions[0]; + } + + return null; + } + + /** + * Returns assertions with and with at least one + * element containing a Method of urn:oasis:names:tc:SAML:2.0:cm:bearer. + * + * @return \LightSaml\Model\Assertion\Assertion[] + */ + public function getBearerAssertions() + { + $result = array(); + if ($this->getAllAssertions()) { + foreach ($this->getAllAssertions() as $assertion) { + if ($assertion->hasBearerSubject()) { + $result[] = $assertion; + } + } // foreach assertions + } + + return $result; + } + + /** + * @param Assertion $assertion + * + * @return Response + */ + public function addAssertion(Assertion $assertion) + { + $this->assertions[] = $assertion; + + return $this; + } + + /** + * @param Assertion $removedAssertion + * + * @return Response + */ + public function removeAssertion(Assertion $removedAssertion) + { + $arr = array(); + $hasThatAssertion = false; + foreach ($this->getAllAssertions() as $assertion) { + if ($assertion !== $removedAssertion) { + $arr[] = $assertion; + } else { + $hasThatAssertion = true; + } + } + + if (false === $hasThatAssertion) { + throw new \InvalidArgumentException('Response does not have assertion specified to be removed'); + } + + return $this; + } + + /** + * @param EncryptedElement $encryptedAssertion + * + * @return Response + */ + public function addEncryptedAssertion(EncryptedElement $encryptedAssertion) + { + $this->encryptedAssertions[] = $encryptedAssertion; + + return $this; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('samlp:Response', SamlConstants::NS_PROTOCOL, $parent, $context); + + parent::serialize($result, $context); + + $this->manyElementsToXml($this->getAllAssertions(), $result, $context, null); + $this->manyElementsToXml($this->getAllEncryptedAssertions(), $result, $context, null); + + // must be done here at the end and not in a base class where declared in order to include signing of the elements added here + $this->singleElementsToXml(array('Signature'), $result, $context); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'Response', SamlConstants::NS_PROTOCOL); + + parent::deserialize($node, $context); + + $this->assertions = array(); + $this->manyElementsFromXml( + $node, + $context, + 'Assertion', + 'saml', + 'LightSaml\Model\Assertion\Assertion', + 'addAssertion' + ); + + $this->encryptedAssertions = array(); + $this->manyElementsFromXml( + $node, + $context, + 'EncryptedAssertion', + 'saml', + 'LightSaml\Model\Assertion\EncryptedAssertionReader', + 'addEncryptedAssertion' + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/SamlMessage.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/SamlMessage.php new file mode 100644 index 0000000000..50573d195d --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/SamlMessage.php @@ -0,0 +1,320 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Protocol; + +use LightSaml\Error\LightSamlXmlException; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Helper; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\Model\Assertion\Issuer; +use LightSaml\Model\SamlElementInterface; +use LightSaml\Model\XmlDSig\Signature; +use LightSaml\SamlConstants; + +abstract class SamlMessage extends AbstractSamlModel +{ + /** @var string */ + protected $id; + + /** @var string */ + protected $version = SamlConstants::VERSION_20; + + /** @var int */ + protected $issueInstant; + + /** @var string|null */ + protected $destination; + + /** @var Issuer|null */ + protected $issuer; + + /** @var string|null */ + protected $consent; + + /** @var Signature|null */ + protected $signature; + + /** @var string|null */ + protected $relayState; + + /** + * @param string $xml + * @param DeserializationContext $context + * + * @return AuthnRequest|LogoutRequest|LogoutResponse|Response|SamlMessage + * + * @throws \Exception + */ + public static function fromXML($xml, DeserializationContext $context) + { + if (false == is_string($xml)) { + throw new \InvalidArgumentException('Expecting string'); + } + + $context->getDocument()->loadXML($xml); + + $node = $context->getDocument()->firstChild; + while ($node && $node instanceof \DOMComment) { + $node = $node->nextSibling; + } + if (null === $node) { + throw new LightSamlXmlException('Empty XML'); + } + + if (SamlConstants::NS_PROTOCOL !== $node->namespaceURI) { + throw new LightSamlXmlException(sprintf( + "Invalid namespace '%s' of the root XML element, expected '%s'", + $context->getDocument()->namespaceURI, + SamlConstants::NS_PROTOCOL + )); + } + + $map = array( + 'AttributeQuery' => null, + 'AuthnRequest' => '\LightSaml\Model\Protocol\AuthnRequest', + 'LogoutResponse' => '\LightSaml\Model\Protocol\LogoutResponse', + 'LogoutRequest' => '\LightSaml\Model\Protocol\LogoutRequest', + 'Response' => '\LightSaml\Model\Protocol\Response', + 'ArtifactResponse' => null, + 'ArtifactResolve' => null, + ); + + $rootElementName = $node->localName; + + if (array_key_exists($rootElementName, $map)) { + if ($class = $map[$rootElementName]) { + /** @var SamlElementInterface $result */ + $result = new $class(); + } else { + throw new \LogicException('Deserialization of %s root element is not implemented'); + } + } else { + throw new LightSamlXmlException(sprintf("Unknown SAML message '%s'", $rootElementName)); + } + + $result->deserialize($node, $context); + + return $result; + } + + /** + * @param string $id + * + * @return SamlMessage + */ + public function setID($id) + { + $this->id = (string) $id; + + return $this; + } + + /** + * @return string + */ + public function getID() + { + return $this->id; + } + + /** + * @param int|string|\DateTime $issueInstant + * + * @return SamlMessage + */ + public function setIssueInstant($issueInstant) + { + $this->issueInstant = Helper::getTimestampFromValue($issueInstant); + + return $this; + } + + /** + * @return int|null + */ + public function getIssueInstantTimestamp() + { + return $this->issueInstant; + } + + /** + * @return string|null + */ + public function getIssueInstantString() + { + if ($this->issueInstant) { + return Helper::time2string($this->issueInstant); + } + + return null; + } + + /** + * @return \DateTime|null + */ + public function getIssueInstantDateTime() + { + if ($this->issueInstant) { + return new \DateTime('@'.$this->issueInstant); + } + + return null; + } + + /** + * @param string $version + * + * @return SamlMessage + */ + public function setVersion($version) + { + $this->version = (string) $version; + + return $this; + } + + /** + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * @param null|string $destination + * + * @return SamlMessage + */ + public function setDestination($destination) + { + $this->destination = $destination; + + return $this; + } + + /** + * @return null|string + */ + public function getDestination() + { + return $this->destination; + } + + /** + * @param Issuer|null $issuer + * + * @return SamlMessage + */ + public function setIssuer(Issuer $issuer = null) + { + $this->issuer = $issuer; + + return $this; + } + + /** + * @return \LightSaml\Model\Assertion\NameID|null + */ + public function getIssuer() + { + return $this->issuer; + } + + /** + * @param null|string $consent + * + * @return StatusResponse + */ + public function setConsent($consent) + { + $this->consent = $consent; + + return $this; + } + + /** + * @return null|string + */ + public function getConsent() + { + return $this->consent; + } + + /** + * @param Signature|null $signature + * + * @return SamlMessage + */ + public function setSignature(Signature $signature = null) + { + $this->signature = $signature; + + return $this; + } + + /** + * @return Signature|null + */ + public function getSignature() + { + return $this->signature; + } + + /** + * @param null|string $relayState + * + * @return SamlMessage + */ + public function setRelayState($relayState) + { + $this->relayState = $relayState; + + return $this; + } + + /** + * @return null|string + */ + public function getRelayState() + { + return $this->relayState; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $this->attributesToXml(array('ID', 'Version', 'IssueInstant', 'Destination', 'Consent'), $parent); + + $this->singleElementsToXml(array('Issuer'), $parent, $context); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->attributesFromXml($node, array('ID', 'Version', 'IssueInstant', 'Destination', 'Consent')); + + $this->singleElementsFromXml($node, $context, array( + 'Issuer' => array('saml', 'LightSaml\Model\Assertion\Issuer'), + 'Signature' => array('ds', 'LightSaml\Model\XmlDSig\SignatureXmlReader'), + )); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/Status.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/Status.php new file mode 100644 index 0000000000..f4f897ba6b --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/Status.php @@ -0,0 +1,120 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Protocol; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\SamlConstants; + +class Status extends AbstractSamlModel +{ + /** @var StatusCode */ + protected $statusCode; + + /** @var string|null */ + protected $statusMessage; + + /** + * @param StatusCode|null $statusCode + * @param string $message + */ + public function __construct(StatusCode $statusCode = null, $message = null) + { + $this->statusCode = $statusCode; + $this->statusMessage = $message; + } + + /** + * @param StatusCode $statusCode + * + * @return Status + */ + public function setStatusCode(StatusCode $statusCode) + { + $this->statusCode = $statusCode; + + return $this; + } + + /** + * @return StatusCode + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * @param string|null $message + */ + public function setStatusMessage($message) + { + $this->statusMessage = (string) $message; + } + + /** + * @return string|null + */ + public function getStatusMessage() + { + return $this->statusMessage; + } + + /** + * @return bool + */ + public function isSuccess() + { + $result = $this->getStatusCode() && SamlConstants::STATUS_SUCCESS == $this->getStatusCode()->getValue(); + + return $result; + } + + /** + * @return Status + */ + public function setSuccess() + { + $this->setStatusCode(new StatusCode()); + $this->getStatusCode()->setValue(SamlConstants::STATUS_SUCCESS); + + return $this; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('samlp:Status', SamlConstants::NS_PROTOCOL, $parent, $context); + + $this->singleElementsToXml(array('StatusCode', 'StatusMessage'), $result, $context, SamlConstants::NS_PROTOCOL); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'Status', SamlConstants::NS_PROTOCOL); + + $this->singleElementsFromXml($node, $context, array( + 'StatusCode' => array('samlp', 'LightSaml\Model\Protocol\StatusCode'), + 'StatusMessage' => array('samlp', null), + )); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/StatusCode.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/StatusCode.php new file mode 100644 index 0000000000..9611a3e59f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/StatusCode.php @@ -0,0 +1,100 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Protocol; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\Model\AbstractSamlModel; +use LightSaml\SamlConstants; + +class StatusCode extends AbstractSamlModel +{ + /** @var string */ + protected $value; + + /** @var StatusCode|null */ + protected $statusCode; + + /** + * @param string $value + */ + public function __construct($value = null) + { + $this->value = $value; + } + + /** + * @param string $value + */ + public function setValue($value) + { + $this->value = (string) $value; + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * @param StatusCode|null $statusCode + * + * @return StatusCode + */ + public function setStatusCode(StatusCode $statusCode) + { + $this->statusCode = $statusCode; + + return $this; + } + + /** + * @return StatusCode|null + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + $result = $this->createElement('samlp:StatusCode', SamlConstants::NS_PROTOCOL, $parent, $context); + + $this->attributesToXml(array('Value'), $result); + + $this->singleElementsToXml(array('StatusCode'), $result, $context); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'StatusCode', SamlConstants::NS_PROTOCOL); + + $this->attributesFromXml($node, array('Value')); + + $this->singleElementsFromXml($node, $context, array( + 'StatusCode' => array('samlp', 'LightSaml\Model\Protocol\StatusCode'), + )); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/StatusResponse.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/StatusResponse.php new file mode 100644 index 0000000000..47b3a96b7e --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/Protocol/StatusResponse.php @@ -0,0 +1,94 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\Protocol; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; + +abstract class StatusResponse extends SamlMessage +{ + /** @var string */ + protected $inResponseTo; + + /** @var Status */ + protected $status; + + /** + * @param string $inResponseTo + * + * @return StatusResponse + */ + public function setInResponseTo($inResponseTo) + { + $this->inResponseTo = $inResponseTo; + + return $this; + } + + /** + * @return string + */ + public function getInResponseTo() + { + return $this->inResponseTo; + } + + /** + * @param Status $status + * + * @return StatusResponse + */ + public function setStatus(Status $status) + { + $this->status = $status; + + return $this; + } + + /** + * @return Status + */ + public function getStatus() + { + return $this->status; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + parent::serialize($parent, $context); + + $this->attributesToXml(array('InResponseTo'), $parent); + + $this->singleElementsToXml(array('Status'), $parent, $context); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->attributesFromXml($node, array('InResponseTo')); + + $this->singleElementsFromXml($node, $context, array( + 'Status' => array('samlp', 'LightSaml\Model\Protocol\Status'), + )); + + parent::deserialize($node, $context); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/SamlElementInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/SamlElementInterface.php new file mode 100644 index 0000000000..3bac0ef83f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/SamlElementInterface.php @@ -0,0 +1,34 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; + +interface SamlElementInterface +{ + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @return void + */ + public function serialize(\DOMNode $parent, SerializationContext $context); + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + * + * @return void + */ + public function deserialize(\DOMNode $node, DeserializationContext $context); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/XmlDSig/AbstractSignatureReader.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/XmlDSig/AbstractSignatureReader.php new file mode 100644 index 0000000000..f83e372a20 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/XmlDSig/AbstractSignatureReader.php @@ -0,0 +1,110 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\XmlDSig; + +use LightSaml\Credential\CredentialInterface; +use LightSaml\Credential\KeyHelper; +use LightSaml\Error\LightSamlSecurityException; +use RobRichards\XMLSecLibs\XMLSecurityKey; + +abstract class AbstractSignatureReader extends Signature +{ + /** @var XMLSecurityKey|null */ + protected $key; + + /** + * @param XMLSecurityKey $key + * + * @return bool True if validated, False if validation was not performed + * + * @throws \LightSaml\Error\LightSamlSecurityException If validation fails + */ + abstract public function validate(XMLSecurityKey $key); + + /** + * @return XMLSecurityKey|null + */ + public function getKey() + { + return $this->key; + } + + /** + * @param CredentialInterface[] $credentialCandidates + * + * @throws \InvalidArgumentException If element of $credentialCandidates array is not CredentialInterface + * @throws \LightSaml\Error\LightSamlSecurityException If validation fails + * + * @return CredentialInterface|null Returns credential that validated the signature or null if validation was not performed + */ + public function validateMulti(array $credentialCandidates) + { + $lastException = null; + + foreach ($credentialCandidates as $credential) { + if (false == $credential instanceof CredentialInterface) { + throw new \InvalidArgumentException('Expected CredentialInterface'); + } + if (null == $credential->getPublicKey()) { + continue; + } + + try { + $result = $this->validate($credential->getPublicKey()); + + if (false === $result) { + return null; + } + + return $credential; + } catch (LightSamlSecurityException $ex) { + $lastException = $ex; + } + } + + if ($lastException) { + throw $lastException; + } else { + throw new LightSamlSecurityException('No public key available for signature verification'); + } + } + + /** + * @return string + */ + abstract public function getAlgorithm(); + + /** + * @param XMLSecurityKey $key + * + * @return XMLSecurityKey + */ + protected function castKeyIfNecessary(XMLSecurityKey $key) + { + $algorithm = $this->getAlgorithm(); + + if (!in_array($algorithm, [ + XMLSecurityKey::RSA_SHA1, + XMLSecurityKey::RSA_SHA256, + XMLSecurityKey::RSA_SHA384, + XMLSecurityKey::RSA_SHA512, + ])) { + throw new LightSamlSecurityException(sprintf('Unsupported signing algorithm: "%s"', $algorithm)); + } + + if ($algorithm != $key->type) { + $key = KeyHelper::castKey($key, $algorithm); + } + + return $key; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/XmlDSig/Signature.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/XmlDSig/Signature.php new file mode 100644 index 0000000000..329524e2fe --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/XmlDSig/Signature.php @@ -0,0 +1,25 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\XmlDSig; + +use LightSaml\Model\AbstractSamlModel; + +abstract class Signature extends AbstractSamlModel +{ + /** + * @return string + */ + protected function getIDName() + { + return 'ID'; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/XmlDSig/SignatureStringReader.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/XmlDSig/SignatureStringReader.php new file mode 100644 index 0000000000..ca05e2e4e3 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/XmlDSig/SignatureStringReader.php @@ -0,0 +1,133 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\XmlDSig; + +use LightSaml\Error\LightSamlSecurityException; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use RobRichards\XMLSecLibs\XMLSecurityKey; + +class SignatureStringReader extends AbstractSignatureReader +{ + /** @var string */ + protected $signature; + + /** @var string */ + protected $algorithm; + + /** @var string */ + protected $data; + + /** + * @param string|null $signature + * @param string|null $algorithm + * @param string|null $data + */ + public function __construct($signature = null, $algorithm = null, $data = null) + { + $this->signature = $signature; + $this->algorithm = $algorithm; + $this->data = $data; + } + + /** + * @param string $algorithm + */ + public function setAlgorithm($algorithm) + { + $this->algorithm = (string) $algorithm; + } + + /** + * @return string + */ + public function getAlgorithm() + { + return $this->algorithm; + } + + /** + * @param string $data + */ + public function setData($data) + { + $this->data = (string) $data; + } + + /** + * @return string + */ + public function getData() + { + return $this->data; + } + + /** + * @param string $signature + */ + public function setSignature($signature) + { + $this->signature = (string) $signature; + } + + /** + * @return string + */ + public function getSignature() + { + return $this->signature; + } + + /** + * @param XMLSecurityKey $key + * + * @return bool True if validated, False if validation was not performed + * + * @throws LightSamlSecurityException If validation fails + */ + public function validate(XMLSecurityKey $key) + { + if (null == $this->getSignature()) { + return false; + } + + $key = $this->castKeyIfNecessary($key); + + $signature = base64_decode($this->getSignature()); + + if (false == $key->verifySignature($this->getData(), $signature)) { + throw new LightSamlSecurityException('Unable to validate signature on query string'); + } + + return true; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @throws \LogicException + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + throw new \LogicException('SignatureStringReader can not be serialized'); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + throw new \LogicException('SignatureStringReader can not be deserialized'); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/XmlDSig/SignatureWriter.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/XmlDSig/SignatureWriter.php new file mode 100644 index 0000000000..4174334977 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/XmlDSig/SignatureWriter.php @@ -0,0 +1,222 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\XmlDSig; + +use LightSaml\Meta\SigningOptions; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\SamlConstants; +use LightSaml\Credential\X509Certificate; +use RobRichards\XMLSecLibs\XMLSecurityKey; +use RobRichards\XMLSecLibs\XMLSecurityDSig; + +class SignatureWriter extends Signature +{ + /** @var string */ + protected $canonicalMethod = XMLSecurityDSig::EXC_C14N; + + /** @var XMLSecurityKey */ + protected $xmlSecurityKey; + + /** @var X509Certificate */ + protected $certificate; + + protected $digestAlgorithm = XMLSecurityDSig::SHA1; + + /** @var SigningOptions */ + protected $signingOptions; + + /** + * @param SigningOptions $options + * + * @return SignatureWriter + */ + public static function create(SigningOptions $options) + { + $writer = new self($options->getCertificate(), $options->getPrivateKey()); + $writer->signingOptions = $options; + + return $writer; + } + + /** + * @param X509Certificate $certificate + * @param XMLSecurityKey $xmlSecurityKey + * + * @return SignatureWriter + */ + public static function createByKeyAndCertificate(X509Certificate $certificate, XMLSecurityKey $xmlSecurityKey) + { + $signingOptions = new SigningOptions($xmlSecurityKey, $certificate); + + return self::create($signingOptions); + } + + /** + * @param X509Certificate|null $certificate + * @param XMLSecurityKey|null $xmlSecurityKey + * @param string $digestAlgorithm + */ + public function __construct(X509Certificate $certificate = null, XMLSecurityKey $xmlSecurityKey = null, $digestAlgorithm = XMLSecurityDSig::SHA1) + { + $this->certificate = $certificate; + $this->xmlSecurityKey = $xmlSecurityKey; + $this->digestAlgorithm = $digestAlgorithm; + } + + /** + * @return string + */ + public function getDigestAlgorithm() + { + return $this->digestAlgorithm; + } + + /** + * @param string $digestAlgorithm + * + * @return SignatureWriter + */ + public function setDigestAlgorithm($digestAlgorithm) + { + $this->digestAlgorithm = $digestAlgorithm; + + return $this; + } + + /** + * @return SigningOptions + */ + public function getSigningOptions() + { + return $this->signingOptions; + } + + /** + * @param SigningOptions $signingOptions + * + * @return SignatureWriter + */ + public function setSigningOptions(SigningOptions $signingOptions) + { + $this->signingOptions = $signingOptions; + + return $this; + } + + /** + * @return string + */ + public function getCanonicalMethod() + { + return $this->canonicalMethod; + } + + /** + * @param string $canonicalMethod + * + * @return SignatureWriter + */ + public function setCanonicalMethod($canonicalMethod) + { + $this->canonicalMethod = $canonicalMethod; + + return $this; + } + + /** + * @param XMLSecurityKey $key + * + * @return SignatureWriter + */ + public function setXmlSecurityKey(XMLSecurityKey $key) + { + $this->xmlSecurityKey = $key; + + return $this; + } + + /** + * @return XMLSecurityKey + */ + public function getXmlSecurityKey() + { + return $this->xmlSecurityKey; + } + + /** + * @param X509Certificate $certificate + * + * @return SignatureWriter + */ + public function setCertificate(X509Certificate $certificate) + { + $this->certificate = $certificate; + + return $this; + } + + /** + * @return X509Certificate + */ + public function getCertificate() + { + return $this->certificate; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + if ($this->signingOptions && false === $this->signingOptions->isEnabled()) { + return; + } + + $objXMLSecDSig = new XMLSecurityDSig(); + $objXMLSecDSig->setCanonicalMethod($this->getCanonicalMethod()); + $key = $this->getXmlSecurityKey(); + + $objXMLSecDSig->addReferenceList( + array($parent), + $this->digestAlgorithm, + array(SamlConstants::XMLSEC_TRANSFORM_ALGORITHM_ENVELOPED_SIGNATURE, XMLSecurityDSig::EXC_C14N), + array('id_name' => $this->getIDName(), 'overwrite' => false) + ); + + $objXMLSecDSig->sign($key); + + $objXMLSecDSig->add509Cert( + $this->getCertificate()->getData(), + false, + false, + $this->signingOptions ? $this->signingOptions->getCertificateOptions()->all() : null + ); + + $firstChild = $parent->hasChildNodes() ? $parent->firstChild : null; + if ($firstChild && 'Issuer' == $firstChild->localName) { + // The signature node should come after the issuer node + $firstChild = $firstChild->nextSibling; + } + $objXMLSecDSig->insertSignature($parent, $firstChild); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + throw new \LogicException('SignatureWriter can not be deserialized'); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Model/XmlDSig/SignatureXmlReader.php b/vendor/lightsaml/lightsaml/src/LightSaml/Model/XmlDSig/SignatureXmlReader.php new file mode 100644 index 0000000000..86efda9f1c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Model/XmlDSig/SignatureXmlReader.php @@ -0,0 +1,158 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Model\XmlDSig; + +use LightSaml\Error\LightSamlSecurityException; +use LightSaml\Error\LightSamlXmlException; +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Context\SerializationContext; +use LightSaml\SamlConstants; +use RobRichards\XMLSecLibs\XMLSecurityKey; +use RobRichards\XMLSecLibs\XMLSecurityDSig; +use RobRichards\XMLSecLibs\XMLSecEnc; + +class SignatureXmlReader extends AbstractSignatureReader +{ + /** @var XMLSecurityDSig */ + protected $signature; + + /** @var string[] */ + protected $certificates = array(); + + /** + * @param string $certificate + */ + public function addCertificate($certificate) + { + $this->certificates[] = (string) $certificate; + } + + /** + * @return \string[] + */ + public function getAllCertificates() + { + return $this->certificates; + } + + /** + * @param XMLSecurityDSig $signature + */ + public function setSignature(XMLSecurityDSig $signature) + { + $this->signature = $signature; + } + + /** + * @return XMLSecurityDSig + */ + public function getSignature() + { + return $this->signature; + } + + /** + * @param XMLSecurityKey $key + * + * @return bool + * + * @throws LightSamlSecurityException + */ + public function validate(XMLSecurityKey $key) + { + if (null == $this->signature) { + return false; + } + + if (false == $this->signature->validateReference()) { + throw new LightSamlSecurityException('Digest validation failed'); + } + + $key = $this->castKeyIfNecessary($key); + + if (false == $this->signature->verify($key)) { + throw new LightSamlSecurityException('Unable to verify Signature'); + } + + return true; + } + + /** + * @return string + * + * @throws \LightSaml\Error\LightSamlXmlException + */ + public function getAlgorithm() + { + $xpath = new \DOMXPath( + $this->signature->sigNode instanceof \DOMDocument + ? $this->signature->sigNode + : $this->signature->sigNode->ownerDocument + ); + $xpath->registerNamespace('ds', XMLSecurityDSig::XMLDSIGNS); + + $list = $xpath->query('./ds:SignedInfo/ds:SignatureMethod', $this->signature->sigNode); + if (!$list || 0 == $list->length) { + throw new LightSamlXmlException('Missing SignatureMethod element'); + } + /** @var $sigMethod \DOMElement */ + $sigMethod = $list->item(0); + if (!$sigMethod->hasAttribute('Algorithm')) { + throw new LightSamlXmlException('Missing Algorithm-attribute on SignatureMethod element.'); + } + $algorithm = $sigMethod->getAttribute('Algorithm'); + + return $algorithm; + } + + /** + * @param \DOMNode $parent + * @param SerializationContext $context + * + * @throws \LogicException + */ + public function serialize(\DOMNode $parent, SerializationContext $context) + { + throw new \LogicException('SignatureXmlReader can not be serialized'); + } + + /** + * @param \DOMNode $node + * @param DeserializationContext $context + * + * @throws \Exception + */ + public function deserialize(\DOMNode $node, DeserializationContext $context) + { + $this->checkXmlNodeName($node, 'Signature', SamlConstants::NS_XMLDSIG); + + $this->signature = new XMLSecurityDSig(); + $this->signature->idKeys[] = $this->getIDName(); + $this->signature->sigNode = $node; + $this->signature->canonicalizeSignedInfo(); + + $this->key = null; + $key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'public')); + XMLSecEnc::staticLocateKeyInfo($key, $node); + if ($key->name || $key->key) { + $this->key = $key; + } + + $this->certificates = array(); + $list = $context->getXpath()->query('./ds:KeyInfo/ds:X509Data/ds:X509Certificate', $node); + foreach ($list as $certNode) { + $certData = trim($certNode->textContent); + $certData = str_replace(array("\r", "\n", "\t", ' '), '', $certData); + $this->certificates[] = $certData; + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Profile/Profiles.php b/vendor/lightsaml/lightsaml/src/LightSaml/Profile/Profiles.php new file mode 100644 index 0000000000..d479219d67 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Profile/Profiles.php @@ -0,0 +1,22 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Profile; + +abstract class Profiles +{ + const METADATA = 'metadata'; + + const SSO_IDP_RECEIVE_AUTHN_REQUEST = 'sso_idp_receive_authn_req'; + const SSO_IDP_SEND_RESPONSE = 'sso_idp_send_response'; + const SSO_SP_SEND_AUTHN_REQUEST = 'sso_sp_send_authn_req'; + const SSO_SP_RECEIVE_RESPONSE = 'sso_sp_receive_response'; +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Attribute/AttributeValueProviderInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Attribute/AttributeValueProviderInterface.php new file mode 100644 index 0000000000..348c43d72e --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Attribute/AttributeValueProviderInterface.php @@ -0,0 +1,25 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\Attribute; + +use LightSaml\Context\Profile\AssertionContext; +use LightSaml\Model\Assertion\Attribute; + +interface AttributeValueProviderInterface +{ + /** + * @param AssertionContext $context + * + * @return Attribute[] + */ + public function getValues(AssertionContext $context); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Attribute/FixedAttributeValueProvider.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Attribute/FixedAttributeValueProvider.php new file mode 100644 index 0000000000..f2a1d7009f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Attribute/FixedAttributeValueProvider.php @@ -0,0 +1,58 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\Attribute; + +use LightSaml\Context\Profile\AssertionContext; +use LightSaml\Model\Assertion\Attribute; + +class FixedAttributeValueProvider implements AttributeValueProviderInterface +{ + /** @var Attribute[] */ + protected $attributes = array(); + + /** + * @param Attribute $attribute + * + * @return FixedAttributeValueProvider + */ + public function add(Attribute $attribute) + { + $this->attributes[] = $attribute; + + return $this; + } + + /** + * @param \LightSaml\Model\Assertion\Attribute[] $attributes + * + * @return FixedAttributeValueProvider + */ + public function setAttributes(array $attributes) + { + $this->attributes = []; + foreach ($attributes as $attribute) { + $this->add($attribute); + } + + return $this; + } + + /** + * @param AssertionContext $context + * + * @return Attribute[] + */ + public function getValues(AssertionContext $context) + { + return $this->attributes; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Credential/CredentialProviderInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Credential/CredentialProviderInterface.php new file mode 100644 index 0000000000..2767290e66 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Credential/CredentialProviderInterface.php @@ -0,0 +1,22 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\Credential; + +use LightSaml\Credential\CredentialInterface; + +interface CredentialProviderInterface +{ + /** + * @return CredentialInterface + */ + public function get(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Credential/X509CredentialFileProvider.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Credential/X509CredentialFileProvider.php new file mode 100644 index 0000000000..ceed3133af --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Credential/X509CredentialFileProvider.php @@ -0,0 +1,64 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\Credential; + +use LightSaml\Credential\X509Credential; +use LightSaml\Credential\KeyHelper; +use LightSaml\Credential\X509Certificate; + +class X509CredentialFileProvider implements CredentialProviderInterface +{ + /** @var string */ + private $entityId; + + /** @var string */ + private $certificatePath; + + /** @var string */ + private $privateKeyPath; + + /** @var string */ + private $privateKeyPassword; + + /** @var X509Credential */ + private $credential; + + /** + * @param string $entityId + * @param string $certificatePath + * @param string $privateKeyPath + * @param $privateKeyPassword + */ + public function __construct($entityId, $certificatePath, $privateKeyPath, $privateKeyPassword) + { + $this->entityId = $entityId; + $this->certificatePath = $certificatePath; + $this->privateKeyPath = $privateKeyPath; + $this->privateKeyPassword = $privateKeyPassword; + } + + /** + * @return X509Credential + */ + public function get() + { + if (null == $this->credential) { + $this->credential = new X509Credential( + X509Certificate::fromFile($this->certificatePath), + KeyHelper::createPrivateKey($this->privateKeyPath, $this->privateKeyPassword, true) + ); + $this->credential->setEntityId($this->entityId); + } + + return $this->credential; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntitiesDescriptor/EntitiesDescriptorProviderInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntitiesDescriptor/EntitiesDescriptorProviderInterface.php new file mode 100644 index 0000000000..beda1455e3 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntitiesDescriptor/EntitiesDescriptorProviderInterface.php @@ -0,0 +1,22 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\EntitiesDescriptor; + +use LightSaml\Model\Metadata\EntitiesDescriptor; + +interface EntitiesDescriptorProviderInterface +{ + /** + * @return EntitiesDescriptor + */ + public function get(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntitiesDescriptor/FileEntitiesDescriptorProvider.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntitiesDescriptor/FileEntitiesDescriptorProvider.php new file mode 100644 index 0000000000..f8a2405519 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntitiesDescriptor/FileEntitiesDescriptorProvider.php @@ -0,0 +1,47 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\EntitiesDescriptor; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Metadata\EntitiesDescriptor; + +class FileEntitiesDescriptorProvider implements EntitiesDescriptorProviderInterface +{ + /** @var string */ + private $filename; + + /** @var EntitiesDescriptor */ + private $entitiesDescriptor; + + /** + * @param string $filename + */ + public function __construct($filename) + { + $this->filename = $filename; + } + + /** + * @return EntitiesDescriptor + */ + public function get() + { + if (null == $this->entitiesDescriptor) { + $this->entitiesDescriptor = new EntitiesDescriptor(); + $deserializationContext = new DeserializationContext(); + $deserializationContext->getDocument()->load($this->filename); + $this->entitiesDescriptor->deserialize($deserializationContext->getDocument()->firstChild, $deserializationContext); + } + + return $this->entitiesDescriptor; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntityDescriptor/EntitiesDescriptorEntityProvider.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntityDescriptor/EntitiesDescriptorEntityProvider.php new file mode 100644 index 0000000000..f057788d05 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntityDescriptor/EntitiesDescriptorEntityProvider.php @@ -0,0 +1,49 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\EntityDescriptor; + +use LightSaml\Model\Metadata\EntityDescriptor; +use LightSaml\Provider\EntitiesDescriptor\EntitiesDescriptorProviderInterface; + +class EntitiesDescriptorEntityProvider implements EntityDescriptorProviderInterface +{ + /** @var EntitiesDescriptorProviderInterface */ + private $entitiesDescriptorProvider; + + /** @var string */ + private $entityId; + + /** @var EntityDescriptor */ + private $entityDescriptor; + + /** + * @param EntitiesDescriptorProviderInterface $entitiesDescriptorProvider + * @param string $entityId + */ + public function __construct(EntitiesDescriptorProviderInterface $entitiesDescriptorProvider, $entityId) + { + $this->entitiesDescriptorProvider = $entitiesDescriptorProvider; + $this->entityId = $entityId; + } + + /** + * @return EntityDescriptor + */ + public function get() + { + if (null == $this->entityDescriptor) { + $this->entityDescriptor = $this->entitiesDescriptorProvider->get()->getByEntityId($this->entityId); + } + + return $this->entityDescriptor; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntityDescriptor/EntityDescriptorProviderInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntityDescriptor/EntityDescriptorProviderInterface.php new file mode 100644 index 0000000000..6a3c91c173 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntityDescriptor/EntityDescriptorProviderInterface.php @@ -0,0 +1,22 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\EntityDescriptor; + +use LightSaml\Model\Metadata\EntityDescriptor; + +interface EntityDescriptorProviderInterface +{ + /** + * @return EntityDescriptor + */ + public function get(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntityDescriptor/FileEntityDescriptorProvider.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntityDescriptor/FileEntityDescriptorProvider.php new file mode 100644 index 0000000000..111722f9fc --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntityDescriptor/FileEntityDescriptorProvider.php @@ -0,0 +1,47 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\EntityDescriptor; + +use LightSaml\Model\Context\DeserializationContext; +use LightSaml\Model\Metadata\EntityDescriptor; + +class FileEntityDescriptorProvider implements EntityDescriptorProviderInterface +{ + /** @var string */ + private $filename; + + /** @var EntityDescriptor|null */ + private $entityDescriptor; + + /** + * @param string $filename + */ + public function __construct($filename) + { + $this->filename = $filename; + } + + /** + * @return EntityDescriptor + */ + public function get() + { + if (null == $this->entityDescriptor) { + $this->entityDescriptor = new EntityDescriptor(); + $deserializationContext = new DeserializationContext(); + $deserializationContext->getDocument()->load($this->filename); + $this->entityDescriptor->deserialize($deserializationContext->getDocument()->firstChild, $deserializationContext); + } + + return $this->entityDescriptor; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntityDescriptor/FileEntityDescriptorProviderFactory.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntityDescriptor/FileEntityDescriptorProviderFactory.php new file mode 100644 index 0000000000..939b26167c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntityDescriptor/FileEntityDescriptorProviderFactory.php @@ -0,0 +1,41 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\EntityDescriptor; + +use LightSaml\Provider\EntitiesDescriptor\FileEntitiesDescriptorProvider; + +class FileEntityDescriptorProviderFactory +{ + /** + * @param string $filename + * + * @return FileEntityDescriptorProvider + */ + public static function fromEntityDescriptorFile($filename) + { + return new FileEntityDescriptorProvider($filename); + } + + /** + * @param string $filename + * @param string $entityId + * + * @return EntitiesDescriptorEntityProvider + */ + public static function fromEntitiesDescriptorFile($filename, $entityId) + { + return new EntitiesDescriptorEntityProvider( + new FileEntitiesDescriptorProvider($filename), + $entityId + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntityDescriptor/FixedEntityDescriptorProvider.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntityDescriptor/FixedEntityDescriptorProvider.php new file mode 100644 index 0000000000..14b02650bb --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/EntityDescriptor/FixedEntityDescriptorProvider.php @@ -0,0 +1,36 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\EntityDescriptor; + +use LightSaml\Model\Metadata\EntityDescriptor; + +class FixedEntityDescriptorProvider implements EntityDescriptorProviderInterface +{ + /** @var EntityDescriptor */ + protected $entityDescriptor; + + /** + * @param EntityDescriptor $entityDescriptor + */ + public function __construct(EntityDescriptor $entityDescriptor) + { + $this->entityDescriptor = $entityDescriptor; + } + + /** + * @return EntityDescriptor + */ + public function get() + { + return $this->entityDescriptor; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/NameID/FixedNameIdProvider.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/NameID/FixedNameIdProvider.php new file mode 100644 index 0000000000..6119b51e99 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/NameID/FixedNameIdProvider.php @@ -0,0 +1,51 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\NameID; + +use LightSaml\Context\Profile\AbstractProfileContext; +use LightSaml\Model\Assertion\NameID; + +class FixedNameIdProvider implements NameIdProviderInterface +{ + /** @var NameID|null */ + protected $nameId; + + /** + * @param NameID|null $nameId + */ + public function __construct(NameID $nameId = null) + { + $this->nameId = $nameId; + } + + /** + * @param NameID|null $nameId + * + * @return FixedNameIdProvider + */ + public function setNameId(NameID $nameId = null) + { + $this->nameId = $nameId; + + return $this; + } + + /** + * @param AbstractProfileContext $context + * + * @return NameID|null + */ + public function getNameID(AbstractProfileContext $context) + { + return $this->nameId; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/NameID/NameIdProviderInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/NameID/NameIdProviderInterface.php new file mode 100644 index 0000000000..7306e05586 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/NameID/NameIdProviderInterface.php @@ -0,0 +1,25 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\NameID; + +use LightSaml\Context\Profile\AbstractProfileContext; +use LightSaml\Model\Assertion\NameID; + +interface NameIdProviderInterface +{ + /** + * @param AbstractProfileContext $context + * + * @return NameID|null + */ + public function getNameID(AbstractProfileContext $context); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Session/FixedSessionInfoProvider.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Session/FixedSessionInfoProvider.php new file mode 100644 index 0000000000..948551de97 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Session/FixedSessionInfoProvider.php @@ -0,0 +1,96 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\Session; + +class FixedSessionInfoProvider implements SessionInfoProviderInterface +{ + /** @var int */ + protected $authnInstant; + + /** @var string */ + protected $sessionIndex; + + /** @var string */ + protected $authnContextClassRef; + + /** + * @param int $authnInstant + * @param string $sessionIndex + * @param string $authnContextClassRef + */ + public function __construct($authnInstant = 0, $sessionIndex = null, $authnContextClassRef = null) + { + $this->authnInstant = $authnInstant; + $this->sessionIndex = $sessionIndex; + $this->authnContextClassRef = $authnContextClassRef; + } + + /** + * @param int $authnInstant + * + * @return FixedSessionInfoProvider + */ + public function setAuthnInstant($authnInstant) + { + $this->authnInstant = intval($authnInstant); + + return $this; + } + + /** + * @param string $sessionIndex + * + * @return FixedSessionInfoProvider + */ + public function setSessionIndex($sessionIndex) + { + $this->sessionIndex = $sessionIndex; + + return $this; + } + + /** + * @param string $authnContextClassRef + * + * @return FixedSessionInfoProvider + */ + public function setAuthnContextClassRef($authnContextClassRef) + { + $this->authnContextClassRef = $authnContextClassRef; + + return $this; + } + + /** + * @return int + */ + public function getAuthnInstant() + { + return $this->authnInstant; + } + + /** + * @return string + */ + public function getSessionIndex() + { + return $this->sessionIndex; + } + + /** + * @return string + */ + public function getAuthnContextClassRef() + { + return $this->authnContextClassRef; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Session/SessionInfoProviderInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Session/SessionInfoProviderInterface.php new file mode 100644 index 0000000000..1ae49a0bb2 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/Session/SessionInfoProviderInterface.php @@ -0,0 +1,30 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\Session; + +interface SessionInfoProviderInterface +{ + /** + * @return int + */ + public function getAuthnInstant(); + + /** + * @return string + */ + public function getSessionIndex(); + + /** + * @return string + */ + public function getAuthnContextClassRef(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/TimeProvider/SystemTimeProvider.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/TimeProvider/SystemTimeProvider.php new file mode 100644 index 0000000000..b3ecedcc11 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/TimeProvider/SystemTimeProvider.php @@ -0,0 +1,31 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\TimeProvider; + +class SystemTimeProvider implements TimeProviderInterface +{ + /** + * @return int + */ + public function getTimestamp() + { + return time(); + } + + /** + * @return \DateTime + */ + public function getDateTime() + { + return new \DateTime(); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Provider/TimeProvider/TimeProviderInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/TimeProvider/TimeProviderInterface.php new file mode 100644 index 0000000000..42c0295f88 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Provider/TimeProvider/TimeProviderInterface.php @@ -0,0 +1,25 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Provider\TimeProvider; + +interface TimeProviderInterface +{ + /** + * @return int + */ + public function getTimestamp(); + + /** + * @return \DateTime + */ + public function getDateTime(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/AbstractCompositeResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/AbstractCompositeResolver.php new file mode 100644 index 0000000000..d28299a048 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/AbstractCompositeResolver.php @@ -0,0 +1,30 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Credential; + +abstract class AbstractCompositeResolver extends AbstractQueryableResolver +{ + /** @var CredentialResolverInterface[] */ + protected $resolvers = array(); + + /** + * @param CredentialResolverInterface $resolver + * + * @return AbstractCompositeResolver + */ + public function add(CredentialResolverInterface $resolver) + { + $this->resolvers[] = $resolver; + + return $this; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/AbstractQueryableResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/AbstractQueryableResolver.php new file mode 100644 index 0000000000..4983dd0a49 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/AbstractQueryableResolver.php @@ -0,0 +1,23 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Credential; + +abstract class AbstractQueryableResolver implements CredentialResolverInterface +{ + /** + * @return CredentialResolverQuery + */ + public function query() + { + return new CredentialResolverQuery($this); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/AlgorithmFilterResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/AlgorithmFilterResolver.php new file mode 100644 index 0000000000..8196148417 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/AlgorithmFilterResolver.php @@ -0,0 +1,46 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Credential; + +use LightSaml\Credential\CredentialInterface; +use LightSaml\Credential\Criteria\AlgorithmCriteria; +use LightSaml\Criteria\CriteriaSet; + +class AlgorithmFilterResolver extends AbstractQueryableResolver +{ + /** + * @param CriteriaSet $criteriaSet + * @param CredentialInterface[] $arrCredentials + * + * @return CredentialInterface[] + */ + public function resolve(CriteriaSet $criteriaSet, array $arrCredentials = array()) + { + if (false == $criteriaSet->has(AlgorithmCriteria::class)) { + return $arrCredentials; + } + + $result = array(); + foreach ($criteriaSet->get(AlgorithmCriteria::class) as $criteria) { + /* @var AlgorithmCriteria $criteria */ + foreach ($arrCredentials as $credential) { + if (($credential->getPrivateKey() && $credential->getPrivateKey()->getAlgorith() == $criteria->getAlgorithm()) || + ($credential->getPublicKey() && $credential->getPublicKey()->getAlgorith() == $criteria->getAlgorithm()) + ) { + $result[] = $credential; + } + } + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/CompositeFilterResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/CompositeFilterResolver.php new file mode 100644 index 0000000000..daa9c3b2a6 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/CompositeFilterResolver.php @@ -0,0 +1,34 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Credential; + +use LightSaml\Credential\CredentialInterface; +use LightSaml\Criteria\CriteriaSet; + +class CompositeFilterResolver extends AbstractCompositeResolver +{ + /** + * @param CriteriaSet $criteriaSet + * @param CredentialInterface[] $arrCredentials + * + * @return CredentialInterface[] + */ + public function resolve(CriteriaSet $criteriaSet, array $arrCredentials = array()) + { + $result = $arrCredentials; + foreach ($this->resolvers as $resolver) { + $result = $resolver->resolve($criteriaSet, $result); + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/CompositeUnionResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/CompositeUnionResolver.php new file mode 100644 index 0000000000..8df4b60bbd --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/CompositeUnionResolver.php @@ -0,0 +1,34 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Credential; + +use LightSaml\Credential\CredentialInterface; +use LightSaml\Criteria\CriteriaSet; + +class CompositeUnionResolver extends AbstractCompositeResolver +{ + /** + * @param CriteriaSet $criteriaSet + * @param CredentialInterface[] $arrCredentials + * + * @return CredentialInterface[] + */ + public function resolve(CriteriaSet $criteriaSet, array $arrCredentials = array()) + { + $result = array(); + foreach ($this->resolvers as $resolver) { + $result = array_merge($result, $resolver->resolve($criteriaSet, $arrCredentials)); + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/CredentialNameFilterResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/CredentialNameFilterResolver.php new file mode 100644 index 0000000000..02fe656d04 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/CredentialNameFilterResolver.php @@ -0,0 +1,49 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Credential; + +use LightSaml\Credential\CredentialInterface; +use LightSaml\Credential\Criteria\CredentialNameCriteria; +use LightSaml\Criteria\CriteriaSet; + +class CredentialNameFilterResolver extends AbstractQueryableResolver +{ + /** + * @param CriteriaSet $criteriaSet + * @param CredentialInterface[] $arrCredentials + * + * @return CredentialInterface[] + */ + public function resolve(CriteriaSet $criteriaSet, array $arrCredentials = array()) + { + if (false == $criteriaSet->has(CredentialNameCriteria::class)) { + return $arrCredentials; + } + + $result = array(); + foreach ($criteriaSet->get(CredentialNameCriteria::class) as $criteria) { + /* @var CredentialNameCriteria $criteria */ + foreach ($arrCredentials as $credential) { + $arrCredentialNames = $credential->getKeyNames(); + $criteriaName = $criteria->getName(); + foreach ($arrCredentialNames as $credentialName) { + if ($credentialName == $criteriaName) { + $result[] = $credential; + break; + } + } + } + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/CredentialResolverInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/CredentialResolverInterface.php new file mode 100644 index 0000000000..5316148d51 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/CredentialResolverInterface.php @@ -0,0 +1,31 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Credential; + +use LightSaml\Credential\CredentialInterface; +use LightSaml\Criteria\CriteriaSet; + +interface CredentialResolverInterface +{ + /** + * @param CriteriaSet $criteriaSet + * @param array|CredentialInterface[] $arrCredentials + * + * @return array|CredentialInterface[] + */ + public function resolve(CriteriaSet $criteriaSet, array $arrCredentials = array()); + + /** + * @return CredentialResolverQuery + */ + public function query(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/CredentialResolverQuery.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/CredentialResolverQuery.php new file mode 100644 index 0000000000..1744ac2463 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/CredentialResolverQuery.php @@ -0,0 +1,98 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Credential; + +use LightSaml\Criteria\CriteriaSet; +use LightSaml\Credential\CredentialInterface; + +class CredentialResolverQuery extends CriteriaSet +{ + /** @var CredentialResolverInterface */ + private $resolver; + + /** @var CredentialInterface[] */ + private $arrCredentials; + + /** + * @param CredentialResolverInterface $resolver + */ + public function __construct(CredentialResolverInterface $resolver) + { + $this->resolver = $resolver; + } + + /** + * @return CredentialResolverQuery + */ + public function resolve() + { + $this->arrCredentials = $this->resolver->resolve($this); + + return $this; + } + + /** + * @return CredentialInterface|null + */ + public function firstCredential() + { + return reset($this->arrCredentials) ?: null; + } + + /** + * @return CredentialInterface[] + */ + public function allCredentials() + { + return $this->arrCredentials; + } + + /** + * @return CredentialInterface[] + */ + public function getPublicKeys() + { + $result = array(); + foreach ($this->arrCredentials as $credential) { + if ($credential instanceof CredentialInterface) { + $publicKey = $credential->getPublicKey(); + if ($publicKey) { + $result[] = $credential; + } + } else { + throw new \InvalidArgumentException('Expected CredentialInterface'); + } + } + + return $result; + } + + /** + * @return CredentialInterface[] + */ + public function getPrivateKeys() + { + $result = array(); + foreach ($this->arrCredentials as $credential) { + if ($credential instanceof CredentialInterface) { + $privateKey = $credential->getPrivateKey(); + if ($privateKey) { + $result[] = $credential; + } + } else { + throw new \InvalidArgumentException('Expected CredentialInterface'); + } + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/EntityIdResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/EntityIdResolver.php new file mode 100644 index 0000000000..9ba61fbbc5 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/EntityIdResolver.php @@ -0,0 +1,48 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Credential; + +use LightSaml\Credential\CredentialInterface; +use LightSaml\Store\Credential\CredentialStoreInterface; +use LightSaml\Criteria\CriteriaSet; +use LightSaml\Credential\Criteria\EntityIdCriteria; + +class EntityIdResolver extends AbstractQueryableResolver +{ + /** @var CredentialStoreInterface */ + protected $credentialStore; + + /** + * @param CredentialStoreInterface $credentialStore + */ + public function __construct(CredentialStoreInterface $credentialStore) + { + $this->credentialStore = $credentialStore; + } + + /** + * @param CriteriaSet $criteriaSet + * @param array|CredentialInterface[] $arrCredentials + * + * @return array|CredentialInterface[] + */ + public function resolve(CriteriaSet $criteriaSet, array $arrCredentials = array()) + { + $result = array(); + foreach ($criteriaSet->get(EntityIdCriteria::class) as $criteria) { + /* @var EntityIdCriteria $criteria */ + $result = array_merge($result, $this->credentialStore->getByEntityId($criteria->getEntityId())); + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/Factory/CredentialResolverFactory.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/Factory/CredentialResolverFactory.php new file mode 100644 index 0000000000..72fa3a7486 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/Factory/CredentialResolverFactory.php @@ -0,0 +1,57 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Credential\Factory; + +use LightSaml\Resolver\Credential\AlgorithmFilterResolver; +use LightSaml\Resolver\Credential\CompositeFilterResolver; +use LightSaml\Resolver\Credential\CredentialNameFilterResolver; +use LightSaml\Resolver\Credential\CredentialResolverInterface; +use LightSaml\Resolver\Credential\EntityIdResolver; +use LightSaml\Resolver\Credential\MetadataFilterResolver; +use LightSaml\Resolver\Credential\PrivateKeyResolver; +use LightSaml\Resolver\Credential\PublicKeyThumbprintResolver; +use LightSaml\Resolver\Credential\UsageFilterResolver; +use LightSaml\Resolver\Credential\X509CredentialResolver; +use LightSaml\Store\Credential\CredentialStoreInterface; + +class CredentialResolverFactory +{ + /** @var CredentialStoreInterface */ + protected $credentialStore; + + /** + * @param CredentialStoreInterface $credentialStore + */ + public function __construct(CredentialStoreInterface $credentialStore) + { + $this->credentialStore = $credentialStore; + } + + /** + * @return CredentialResolverInterface + */ + public function build() + { + $result = (new CompositeFilterResolver()) + ->add(new EntityIdResolver($this->credentialStore)) + ->add(new AlgorithmFilterResolver()) + ->add(new CredentialNameFilterResolver()) + ->add(new MetadataFilterResolver()) + ->add(new UsageFilterResolver()) + ->add(new PrivateKeyResolver()) + ->add(new X509CredentialResolver()) + ->add(new PublicKeyThumbprintResolver()) + ; + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/MetadataFilterResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/MetadataFilterResolver.php new file mode 100644 index 0000000000..d3a0946c67 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/MetadataFilterResolver.php @@ -0,0 +1,52 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Credential; + +use LightSaml\Model\Metadata\IdpSsoDescriptor; +use LightSaml\Model\Metadata\SpSsoDescriptor; +use LightSaml\Credential\Context\MetadataCredentialContext; +use LightSaml\Credential\CredentialInterface; +use LightSaml\Criteria\CriteriaSet; +use LightSaml\Credential\Criteria\MetadataCriteria; + +class MetadataFilterResolver extends AbstractQueryableResolver +{ + /** + * @param CriteriaSet $criteriaSet + * @param CredentialInterface[] $arrCredentials + * + * @return CredentialInterface[] + */ + public function resolve(CriteriaSet $criteriaSet, array $arrCredentials = array()) + { + if (false == $criteriaSet->has(MetadataCriteria::class)) { + return $arrCredentials; + } + + $result = array(); + foreach ($criteriaSet->get(MetadataCriteria::class) as $criteria) { + /* @var MetadataCriteria $criteria */ + foreach ($arrCredentials as $credential) { + /** @var MetadataCredentialContext $metadataContext */ + $metadataContext = $credential->getCredentialContext()->get(MetadataCredentialContext::class); + if (false == $metadataContext || + MetadataCriteria::TYPE_IDP == $criteria->getMetadataType() && $metadataContext->getRoleDescriptor() instanceof IdpSsoDescriptor || + MetadataCriteria::TYPE_SP == $criteria->getMetadataType() && $metadataContext->getRoleDescriptor() instanceof SpSsoDescriptor + ) { + $result[] = $credential; + } + } + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/PrivateKeyResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/PrivateKeyResolver.php new file mode 100644 index 0000000000..148161e55f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/PrivateKeyResolver.php @@ -0,0 +1,41 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Credential; + +use LightSaml\Credential\CredentialInterface; +use LightSaml\Criteria\CriteriaSet; +use LightSaml\Credential\Criteria\PrivateKeyCriteria; + +class PrivateKeyResolver extends AbstractQueryableResolver +{ + /** + * @param CriteriaSet $criteriaSet + * @param CredentialInterface[] $arrCredentials + * + * @return CredentialInterface[] + */ + public function resolve(CriteriaSet $criteriaSet, array $arrCredentials = array()) + { + if (false == $criteriaSet->has(PrivateKeyCriteria::class)) { + return $arrCredentials; + } + + $result = array(); + foreach ($arrCredentials as $credential) { + if ($credential->getPrivateKey()) { + $result[] = $credential; + } + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/PublicKeyThumbprintResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/PublicKeyThumbprintResolver.php new file mode 100644 index 0000000000..876050919f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/PublicKeyThumbprintResolver.php @@ -0,0 +1,44 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Credential; + +use LightSaml\Credential\CredentialInterface; +use LightSaml\Credential\Criteria\PublicKeyThumbprintCriteria; +use LightSaml\Criteria\CriteriaSet; + +class PublicKeyThumbprintResolver extends AbstractQueryableResolver +{ + /** + * @param CriteriaSet $criteriaSet + * @param CredentialInterface[] $arrCredentials + * + * @return CredentialInterface[] + */ + public function resolve(CriteriaSet $criteriaSet, array $arrCredentials = array()) + { + if (false == $criteriaSet->has(PublicKeyThumbprintCriteria::class)) { + return $arrCredentials; + } + + $result = array(); + /** @var PublicKeyThumbprintCriteria $criteria */ + foreach ($criteriaSet->get(PublicKeyThumbprintCriteria::class) as $criteria) { + foreach ($arrCredentials as $credential) { + if ($credential->getPublicKey() && $credential->getPublicKey()->getX509Thumbprint() == $criteria->getThumbprint()) { + $result[] = $credential; + } + } + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/UsageFilterResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/UsageFilterResolver.php new file mode 100644 index 0000000000..446723d4c0 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/UsageFilterResolver.php @@ -0,0 +1,44 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Credential; + +use LightSaml\Credential\CredentialInterface; +use LightSaml\Criteria\CriteriaSet; +use LightSaml\Credential\Criteria\UsageCriteria; + +class UsageFilterResolver extends AbstractQueryableResolver +{ + /** + * @param CriteriaSet $criteriaSet + * @param CredentialInterface[] $arrCredentials + * + * @return CredentialInterface[] + */ + public function resolve(CriteriaSet $criteriaSet, array $arrCredentials = array()) + { + if (false == $criteriaSet->has(UsageCriteria::class)) { + return $arrCredentials; + } + + $result = array(); + foreach ($criteriaSet->get(UsageCriteria::class) as $criteria) { + /* @var UsageCriteria $criteria */ + foreach ($arrCredentials as $credential) { + if (false == $credential->getUsageType() || $criteria->getUsage() == $credential->getUsageType()) { + $result[] = $credential; + } + } + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/X509CredentialResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/X509CredentialResolver.php new file mode 100644 index 0000000000..7e85dc3603 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Credential/X509CredentialResolver.php @@ -0,0 +1,42 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Credential; + +use LightSaml\Criteria\CriteriaSet; +use LightSaml\Credential\CredentialInterface; +use LightSaml\Credential\X509CredentialInterface; +use LightSaml\Credential\Criteria\X509CredentialCriteria; + +class X509CredentialResolver extends AbstractQueryableResolver +{ + /** + * @param CriteriaSet $criteriaSet + * @param CredentialInterface[] $arrCredentials + * + * @return CredentialInterface[] + */ + public function resolve(CriteriaSet $criteriaSet, array $arrCredentials = array()) + { + if (false == $criteriaSet->has(X509CredentialCriteria::class)) { + return $arrCredentials; + } + + $result = array(); + foreach ($arrCredentials as $credential) { + if ($credential instanceof X509CredentialInterface) { + $result[] = $credential; + } + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/BindingEndpointResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/BindingEndpointResolver.php new file mode 100644 index 0000000000..d3ecc90468 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/BindingEndpointResolver.php @@ -0,0 +1,54 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Endpoint; + +use LightSaml\Model\Metadata\EndpointReference; +use LightSaml\Criteria\CriteriaSet; +use LightSaml\Resolver\Endpoint\Criteria\BindingCriteria; + +class BindingEndpointResolver implements EndpointResolverInterface +{ + /** + * @param CriteriaSet $criteriaSet + * @param EndpointReference[] $candidates + * + * @return EndpointReference[] + */ + public function resolve(CriteriaSet $criteriaSet, array $candidates) + { + if (false === $criteriaSet->has(BindingCriteria::class)) { + return $candidates; + } + + $arrOrdered = array(); + /** @var BindingCriteria $bindingCriteria */ + foreach ($criteriaSet->get(BindingCriteria::class) as $bindingCriteria) { + foreach ($candidates as $endpointReference) { + $preference = $bindingCriteria->getPreference($endpointReference->getEndpoint()->getBinding()); + if (null !== $preference) { + $arrOrdered[$preference][] = $endpointReference; + } + } + } + + ksort($arrOrdered); + + $result = array(); + foreach ($arrOrdered as $arr) { + foreach ($arr as $endpointReference) { + $result[] = $endpointReference; + } + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/CompositeEndpointResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/CompositeEndpointResolver.php new file mode 100644 index 0000000000..3e7b6d3164 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/CompositeEndpointResolver.php @@ -0,0 +1,58 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Endpoint; + +use LightSaml\Criteria\CriteriaSet; +use LightSaml\Model\Metadata\EndpointReference; + +class CompositeEndpointResolver implements EndpointResolverInterface +{ + /** @var EndpointResolverInterface[] */ + protected $resolvers = array(); + + /** + * @param EndpointResolverInterface[] $builders + */ + public function __construct(array $builders = array()) + { + $this->resolvers = $builders; + } + + /** + * @param EndpointResolverInterface $builder + * + * @return CompositeEndpointResolver + */ + public function add(EndpointResolverInterface $builder) + { + $this->resolvers[] = $builder; + + return $this; + } + + /** + * @param CriteriaSet $criteriaSet + * @param EndpointReference[] $candidates + * + * @return EndpointReference[] + */ + public function resolve(CriteriaSet $criteriaSet, array $candidates) + { + $result = $candidates; + + foreach ($this->resolvers as $resolver) { + $result = $resolver->resolve($criteriaSet, $result); + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/Criteria/BindingCriteria.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/Criteria/BindingCriteria.php new file mode 100644 index 0000000000..747e1a19e6 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/Criteria/BindingCriteria.php @@ -0,0 +1,68 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Endpoint\Criteria; + +use LightSaml\Criteria\CriteriaInterface; + +class BindingCriteria implements CriteriaInterface +{ + /** + * Binding => Preference. + * + * @var int[] + */ + protected $bindings = []; + + /** + * @param string[] $bindings Ordered by preference, first being most preferable, last least preferable + */ + public function __construct(array $bindings) + { + $this->bindings = array(); + + foreach ($bindings as $binding) { + $this->add($binding); + } + } + + /** + * @param string $binding Next preferable binding + * + * @return BindingCriteria + */ + public function add($binding) + { + $this->bindings[$binding] = count($this->bindings) + 1; + + return $this; + } + + /** + * Returns array of bindings ordered by preference, first being most preferable, last least preferable. + * + * @return string[] + */ + public function getAllBindings() + { + return array_keys($this->bindings); + } + + /** + * @param $binding + * + * @return int|null Preference of a binding or null if not preferred + */ + public function getPreference($binding) + { + return isset($this->bindings[$binding]) ? $this->bindings[$binding] : null; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/Criteria/DescriptorTypeCriteria.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/Criteria/DescriptorTypeCriteria.php new file mode 100644 index 0000000000..70310d7401 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/Criteria/DescriptorTypeCriteria.php @@ -0,0 +1,36 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Endpoint\Criteria; + +use LightSaml\Criteria\CriteriaInterface; + +class DescriptorTypeCriteria implements CriteriaInterface +{ + /** @var string */ + protected $descriptorType; + + /** + * @param string $descriptorType + */ + public function __construct($descriptorType) + { + $this->descriptorType = $descriptorType; + } + + /** + * @return string + */ + public function getDescriptorType() + { + return $this->descriptorType; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/Criteria/IndexCriteria.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/Criteria/IndexCriteria.php new file mode 100644 index 0000000000..7bacacb7e8 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/Criteria/IndexCriteria.php @@ -0,0 +1,36 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Endpoint\Criteria; + +use LightSaml\Criteria\CriteriaInterface; + +class IndexCriteria implements CriteriaInterface +{ + /** @var string */ + protected $index; + + /** + * @param string $index + */ + public function __construct($index) + { + $this->index = $index; + } + + /** + * @return string + */ + public function getIndex() + { + return $this->index; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/Criteria/LocationCriteria.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/Criteria/LocationCriteria.php new file mode 100644 index 0000000000..56426e01a8 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/Criteria/LocationCriteria.php @@ -0,0 +1,36 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Endpoint\Criteria; + +use LightSaml\Criteria\CriteriaInterface; + +class LocationCriteria implements CriteriaInterface +{ + /** @var string */ + protected $location; + + /** + * @param string $location + */ + public function __construct($location) + { + $this->location = $location; + } + + /** + * @return string + */ + public function getLocation() + { + return $this->location; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/Criteria/ServiceTypeCriteria.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/Criteria/ServiceTypeCriteria.php new file mode 100644 index 0000000000..c58a3aef18 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/Criteria/ServiceTypeCriteria.php @@ -0,0 +1,36 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Endpoint\Criteria; + +use LightSaml\Criteria\CriteriaInterface; + +class ServiceTypeCriteria implements CriteriaInterface +{ + /** @var string */ + protected $serviceType; + + /** + * @param string $serviceType + */ + public function __construct($serviceType) + { + $this->serviceType = $serviceType; + } + + /** + * @return string + */ + public function getServiceType() + { + return $this->serviceType; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/DescriptorTypeEndpointResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/DescriptorTypeEndpointResolver.php new file mode 100644 index 0000000000..f3cfd9ce47 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/DescriptorTypeEndpointResolver.php @@ -0,0 +1,50 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Endpoint; + +use LightSaml\Criteria\CriteriaSet; +use LightSaml\Model\Metadata\EndpointReference; +use LightSaml\Resolver\Endpoint\Criteria\DescriptorTypeCriteria; + +/** + * Filters out candidate endpoints which RoleDescriptor does not match with type specified + * in the DescriptorTypeCriteria. If criteria set does not have DescriptorTypeCriteria + * it will return all endpoint candidates. + */ +class DescriptorTypeEndpointResolver implements EndpointResolverInterface +{ + /** + * @param CriteriaSet $criteriaSet + * @param EndpointReference[] $candidates + * + * @return EndpointReference[] + */ + public function resolve(CriteriaSet $criteriaSet, array $candidates) + { + if (false === $criteriaSet->has(DescriptorTypeCriteria::class)) { + return $candidates; + } + + $result = array(); + /** @var DescriptorTypeCriteria $descriptorTypeCriteria */ + foreach ($criteriaSet->get(DescriptorTypeCriteria::class) as $descriptorTypeCriteria) { + foreach ($candidates as $endpointReference) { + $type = $descriptorTypeCriteria->getDescriptorType(); + if ($endpointReference->getDescriptor() instanceof $type) { + $result[] = $endpointReference; + } + } + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/EndpointResolverInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/EndpointResolverInterface.php new file mode 100644 index 0000000000..bce1cca253 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/EndpointResolverInterface.php @@ -0,0 +1,26 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Endpoint; + +use LightSaml\Criteria\CriteriaSet; +use LightSaml\Model\Metadata\EndpointReference; + +interface EndpointResolverInterface +{ + /** + * @param CriteriaSet $criteriaSet + * @param EndpointReference[] $candidates + * + * @return EndpointReference[] + */ + public function resolve(CriteriaSet $criteriaSet, array $candidates); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/IndexEndpointResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/IndexEndpointResolver.php new file mode 100644 index 0000000000..895b74db88 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/IndexEndpointResolver.php @@ -0,0 +1,48 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Endpoint; + +use LightSaml\Model\Metadata\EndpointReference; +use LightSaml\Model\Metadata\IndexedEndpoint; +use LightSaml\Resolver\Endpoint\Criteria\IndexCriteria; +use LightSaml\Criteria\CriteriaSet; + +class IndexEndpointResolver implements EndpointResolverInterface +{ + /** + * @param CriteriaSet $criteriaSet + * @param EndpointReference[] $candidates + * + * @return EndpointReference[] + */ + public function resolve(CriteriaSet $criteriaSet, array $candidates) + { + if (false === $criteriaSet->has(IndexCriteria::class)) { + return $candidates; + } + + $result = array(); + /** @var IndexCriteria $indexCriteria */ + foreach ($criteriaSet->get(IndexCriteria::class) as $indexCriteria) { + foreach ($candidates as $endpointReference) { + $endpoint = $endpointReference->getEndpoint(); + if ($endpoint instanceof IndexedEndpoint) { + if ($endpoint->getIndex() == $indexCriteria->getIndex()) { + $result[] = $endpointReference; + } + } + } + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/LocationEndpointResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/LocationEndpointResolver.php new file mode 100644 index 0000000000..01ec70d0c6 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/LocationEndpointResolver.php @@ -0,0 +1,44 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Endpoint; + +use LightSaml\Model\Metadata\EndpointReference; +use LightSaml\Resolver\Endpoint\Criteria\LocationCriteria; +use LightSaml\Criteria\CriteriaSet; + +class LocationEndpointResolver implements EndpointResolverInterface +{ + /** + * @param CriteriaSet $criteriaSet + * @param EndpointReference[] $candidates + * + * @return EndpointReference[] + */ + public function resolve(CriteriaSet $criteriaSet, array $candidates) + { + if (false === $criteriaSet->has(LocationCriteria::class)) { + return $candidates; + } + + $result = array(); + /** @var LocationCriteria $locationCriteria */ + foreach ($criteriaSet->get(LocationCriteria::class) as $locationCriteria) { + foreach ($candidates as $endpointReference) { + if ($endpointReference->getEndpoint()->getLocation() == $locationCriteria->getLocation()) { + $result[] = $endpointReference; + } + } + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/ServiceTypeEndpointResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/ServiceTypeEndpointResolver.php new file mode 100644 index 0000000000..05fac69152 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Endpoint/ServiceTypeEndpointResolver.php @@ -0,0 +1,50 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Endpoint; + +use LightSaml\Criteria\CriteriaSet; +use LightSaml\Model\Metadata\EndpointReference; +use LightSaml\Resolver\Endpoint\Criteria\ServiceTypeCriteria; + +/** + * Filters out those endpoint candidates which are not an instance of the type + * specified in the ServiceTypeCriteria. If criteria set does not have + * ServiceTypeCriteria it will return all endpoint candidates. + */ +class ServiceTypeEndpointResolver implements EndpointResolverInterface +{ + /** + * @param CriteriaSet $criteriaSet + * @param EndpointReference[] $candidates + * + * @return EndpointReference[] + */ + public function resolve(CriteriaSet $criteriaSet, array $candidates) + { + if (false === $criteriaSet->has(ServiceTypeCriteria::class)) { + return $candidates; + } + + $result = array(); + /** @var ServiceTypeCriteria $serviceTypeCriteria */ + foreach ($criteriaSet->get(ServiceTypeCriteria::class) as $serviceTypeCriteria) { + foreach ($candidates as $endpointReference) { + $type = $serviceTypeCriteria->getServiceType(); + if ($endpointReference->getEndpoint() instanceof $type) { + $result[] = $endpointReference; + } + } + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Session/SessionProcessor.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Session/SessionProcessor.php new file mode 100644 index 0000000000..b0d0770606 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Session/SessionProcessor.php @@ -0,0 +1,148 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Session; + +use LightSaml\Model\Assertion\Assertion; +use LightSaml\Provider\TimeProvider\TimeProviderInterface; +use LightSaml\State\Sso\SsoSessionState; +use LightSaml\State\Sso\SsoState; +use LightSaml\Store\Sso\SsoStateStoreInterface; + +class SessionProcessor implements SessionProcessorInterface +{ + /** @var SsoStateStoreInterface */ + protected $ssoStateStore; + + /** @var TimeProviderInterface */ + protected $timeProvider; + + /** + * @param SsoStateStoreInterface $ssoStateStore + * @param TimeProviderInterface $timeProvider + */ + public function __construct(SsoStateStoreInterface $ssoStateStore, TimeProviderInterface $timeProvider) + { + $this->ssoStateStore = $ssoStateStore; + $this->timeProvider = $timeProvider; + } + + /** + * @param Assertion[] $assertions + * @param string $ownEntityId + * @param string $partyEntityId + */ + public function processAssertions(array $assertions, $ownEntityId, $partyEntityId) + { + $now = $this->timeProvider->getDateTime()->setTimezone(new \DateTimeZone('GMT')); + $ssoState = $this->ssoStateStore->get(); + + foreach ($assertions as $assertion) { + if ($assertion instanceof Assertion) { + if ($this->supportsSession($assertion)) { + $this->checkSession($ownEntityId, $partyEntityId, $ssoState, $assertion, $now); + } + } else { + throw new \InvalidArgumentException('Expected Assertion'); + } + } + + $this->ssoStateStore->set($ssoState); + } + + /** + * @param Assertion $assertion + * + * @return bool + */ + protected function supportsSession(Assertion $assertion) + { + return + $assertion->hasBearerSubject() && + null != $assertion->getSubject() && + null != $assertion->getSubject()->getNameID() + ; + } + + /** + * @param string $ownEntityId + * @param string $partyEntityId + * @param SsoState $ssoState + * @param Assertion $assertion + * @param \DateTime $now + */ + protected function checkSession($ownEntityId, $partyEntityId, SsoState $ssoState, Assertion $assertion, \DateTime $now) + { + $sessions = $this->filterSessions($ssoState, $assertion, $ownEntityId, $partyEntityId); + + if (empty($sessions)) { + $this->createSession($ssoState, $assertion, $now, $ownEntityId, $partyEntityId); + } else { + $this->updateLastAuthn($sessions, $now); + } + } + + /** + * @param SsoState $ssoState + * @param Assertion $assertion + * @param \DateTime $now + * @param string $ownEntityId + * @param string $partyEntityId + * + * @return SsoSessionState + */ + protected function createSession(SsoState $ssoState, Assertion $assertion, \DateTime $now, $ownEntityId, $partyEntityId) + { + $ssoSession = new SsoSessionState(); + $ssoSession->setIdpEntityId($partyEntityId) + ->setSpEntityId($ownEntityId) + ->setNameId($assertion->getSubject()->getNameID()->getValue()) + ->setNameIdFormat($assertion->getSubject()->getNameID()->getFormat()) + ->setSessionIndex($assertion->getFirstAuthnStatement()->getSessionIndex()) + ->setSessionInstant($assertion->getFirstAuthnStatement()->getAuthnInstantDateTime()) + ->setFirstAuthOn($now) + ->setLastAuthOn($now) + ; + $ssoState->addSsoSession($ssoSession); + + return $ssoSession; + } + + /** + * @param SsoSessionState[] $sessions + * @param \DateTime $now + */ + protected function updateLastAuthn(array $sessions, \DateTime $now) + { + foreach ($sessions as $session) { + $session->setLastAuthOn($now); + } + } + + /** + * @param SsoState $ssoState + * @param Assertion $assertion + * @param string $ownEntityId + * @param string $partyEntityId + * + * @return \LightSaml\State\Sso\SsoSessionState[] + */ + protected function filterSessions(SsoState $ssoState, Assertion $assertion, $ownEntityId, $partyEntityId) + { + return $ssoState->filter( + $partyEntityId, + $ownEntityId, + $assertion->getSubject()->getNameID()->getValue(), + $assertion->getSubject()->getNameID()->getFormat(), + $assertion->getFirstAuthnStatement()->getSessionIndex() + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Session/SessionProcessorInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Session/SessionProcessorInterface.php new file mode 100644 index 0000000000..f9f916dc0c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Session/SessionProcessorInterface.php @@ -0,0 +1,24 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Session; + +use LightSaml\Model\Assertion\Assertion; + +interface SessionProcessorInterface +{ + /** + * @param Assertion[] $assertions + * @param string $ownEntityId + * @param string $partyEntityId + */ + public function processAssertions(array $assertions, $ownEntityId, $partyEntityId); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Signature/OwnSignatureResolver.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Signature/OwnSignatureResolver.php new file mode 100644 index 0000000000..dbcf882d31 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Signature/OwnSignatureResolver.php @@ -0,0 +1,90 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Signature; + +use LightSaml\Context\Profile\AbstractProfileContext; +use LightSaml\Context\Profile\ProfileContext; +use LightSaml\Error\LightSamlContextException; +use LightSaml\Model\XmlDSig\SignatureWriter; +use LightSaml\Resolver\Credential\CredentialResolverInterface; +use LightSaml\SamlConstants; +use LightSaml\Credential\UsageType; +use LightSaml\Credential\X509CredentialInterface; +use LightSaml\Credential\Criteria\EntityIdCriteria; +use LightSaml\Credential\Criteria\MetadataCriteria; +use LightSaml\Credential\Criteria\UsageCriteria; +use LightSaml\Credential\Criteria\X509CredentialCriteria; + +class OwnSignatureResolver implements SignatureResolverInterface +{ + /** @var CredentialResolverInterface */ + protected $credentialResolver; + + /** + * @param CredentialResolverInterface $credentialResolver + */ + public function __construct(CredentialResolverInterface $credentialResolver) + { + $this->credentialResolver = $credentialResolver; + } + + /** + * @param AbstractProfileContext $context + * + * @return SignatureWriter + */ + public function getSignature(AbstractProfileContext $context) + { + $credential = $this->getSigningCredential($context); + if (null == $credential) { + throw new LightSamlContextException($context, 'Unable to find signing credential'); + } + $trustOptions = $context->getProfileContext()->getTrustOptions(); + + $signature = new SignatureWriter($credential->getCertificate(), $credential->getPrivateKey(), $trustOptions->getSignatureDigestAlgorithm()); + + return $signature; + } + + /** + * @param AbstractProfileContext $context + * + * @return X509CredentialInterface|null + */ + private function getSigningCredential(AbstractProfileContext $context) + { + $profileContext = $context->getProfileContext(); + + $entityDescriptor = $profileContext->getOwnEntityDescriptor(); + + $query = $this->credentialResolver->query(); + $query + ->add(new EntityIdCriteria($entityDescriptor->getEntityID())) + ->add(new UsageCriteria(UsageType::SIGNING)) + ->add(new X509CredentialCriteria()) + ->addIf(ProfileContext::ROLE_IDP === $profileContext->getOwnRole(), function () { + return new MetadataCriteria(MetadataCriteria::TYPE_IDP, SamlConstants::VERSION_20); + }) + ->addIf(ProfileContext::ROLE_SP === $profileContext->getOwnRole(), function () { + return new MetadataCriteria(MetadataCriteria::TYPE_SP, SamlConstants::VERSION_20); + }) + ; + $query->resolve(); + + $result = $query->firstCredential(); + if ($result && false === $result instanceof X509CredentialInterface) { + throw new \LogicException(sprintf('Expected X509CredentialInterface but got %s', get_class($result))); + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Signature/SignatureResolverInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Signature/SignatureResolverInterface.php new file mode 100644 index 0000000000..3738d4767d --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Resolver/Signature/SignatureResolverInterface.php @@ -0,0 +1,25 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Resolver\Signature; + +use LightSaml\Context\Profile\AbstractProfileContext; +use LightSaml\Model\XmlDSig\SignatureWriter; + +interface SignatureResolverInterface +{ + /** + * @param AbstractProfileContext $context + * + * @return SignatureWriter|null + */ + public function getSignature(AbstractProfileContext $context); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/SamlConstants.php b/vendor/lightsaml/lightsaml/src/LightSaml/SamlConstants.php new file mode 100644 index 0000000000..7816fa6b8b --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/SamlConstants.php @@ -0,0 +1,235 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml; + +abstract class SamlConstants +{ + const PROTOCOL_SAML2 = 'urn:oasis:names:tc:SAML:2.0:protocol'; + const PROTOCOL_SAML1 = 'urn:oasis:names:tc:SAML:1.0:protocol'; + const PROTOCOL_SAML11 = 'urn:oasis:names:tc:SAML:1.1:protocol'; + const PROTOCOL_SHIB1 = 'urn:mace:shibboleth:1.0'; + const PROTOCOL_WS_FED = 'http://schemas.xmlsoap.org/ws/2003/07/secext???'; + + const VERSION_20 = '2.0'; + + const NS_PROTOCOL = 'urn:oasis:names:tc:SAML:2.0:protocol'; + const NS_METADATA = 'urn:oasis:names:tc:SAML:2.0:metadata'; + const NS_ASSERTION = 'urn:oasis:names:tc:SAML:2.0:assertion'; + const NS_XMLDSIG = 'http://www.w3.org/2000/09/xmldsig#'; + + const NAME_ID_FORMAT_NONE = null; + const NAME_ID_FORMAT_ENTITY = 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity'; + const NAME_ID_FORMAT_PERSISTENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'; + const NAME_ID_FORMAT_TRANSIENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'; + const NAME_ID_FORMAT_EMAIL = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'; + const NAME_ID_FORMAT_SHIB_NAME_ID = 'urn:mace:shibboleth:1.0:nameIdentifier'; + const NAME_ID_FORMAT_X509_SUBJECT_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName'; + const NAME_ID_FORMAT_WINDOWS = 'urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName'; + const NAME_ID_FORMAT_KERBEROS = 'urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos'; + const NAME_ID_FORMAT_UNSPECIFIED = 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified'; + + const BINDING_SAML2_HTTP_REDIRECT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'; + const BINDING_SAML2_HTTP_POST = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'; + const BINDING_SAML2_HTTP_ARTIFACT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact'; + const BINDING_SAML2_SOAP = 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP'; + const BINDING_SAML2_HTTP_POST_SIMPLE_SIGN = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign'; + const BINDING_SHIB1_AUTHN_REQUEST = 'urn:mace:shibboleth:1.0:profiles:AuthnRequest'; + const BINDING_SAML1_BROWSER_POST = 'urn:oasis:names:tc:SAML:1.0:profiles:browser-post'; + const BINDING_SAML1_ARTIFACT1 = 'urn:oasis:names:tc:SAML:1.0:profiles:artifact-01'; + const BINDING_WS_FED_WEB_SVC = 'http://schemas.xmlsoap.org/ws/2003/07/secext'; + + const STATUS_SUCCESS = 'urn:oasis:names:tc:SAML:2.0:status:Success'; + const STATUS_REQUESTER = 'urn:oasis:names:tc:SAML:2.0:status:Requester'; + const STATUS_RESPONDER = 'urn:oasis:names:tc:SAML:2.0:status:Responder'; + const STATUS_VERSION_MISMATCH = 'urn:oasis:names:tc:SAML:2.0:status:VersionMismatch'; + const STATUS_NO_PASSIVE = 'urn:oasis:names:tc:SAML:2.0:status:NoPassive'; + const STATUS_PARTIAL_LOGOUT = 'urn:oasis:names:tc:SAML:2.0:status:PartialLogout'; + const STATUS_PROXY_COUNT_EXCEEDED = 'urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded'; + const STATUS_INVALID_NAME_ID_POLICY = 'urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy'; + const STATUS_UNSUPPORTED_BINDING = 'urn:oasis:names:tc:SAML:2.0:status:UnsupportedBinding'; + + const XMLSEC_TRANSFORM_ALGORITHM_ENVELOPED_SIGNATURE = 'http://www.w3.org/2000/09/xmldsig#enveloped-signature'; + + const CONSENT_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:consent:unspecified'; + + const CONFIRMATION_METHOD_BEARER = 'urn:oasis:names:tc:SAML:2.0:cm:bearer'; + const CONFIRMATION_METHOD_HOK = 'urn:oasis:names:tc:SAML:2.0:cm:holder-of-key'; + const CONFIRMATION_METHOD_SENDER_VOUCHES = 'urn:oasis:names:tc:SAML:2.0:cm:sender-vouches'; + + const AUTHN_CONTEXT_PASSWORD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password'; + const AUTHN_CONTEXT_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified'; + const AUTHN_CONTEXT_PASSWORD_PROTECTED_TRANSPORT = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'; + const AUTHN_CONTEXT_WINDOWS = 'urn:federation:authentication:windows'; + + const ENCODING_DEFLATE = 'urn:oasis:names:tc:SAML:2.0:bindings:URL-Encoding:DEFLATE'; + + const LOGOUT_REASON_USER = 'urn:oasis:names:tc:SAML:2.0:logout:user'; + const LOGOUT_REASON_ADMIN = 'urn:oasis:names:tc:SAML:2.0:logout:admin'; + const LOGOUT_REASON_GLOBAL_TIMEOUT = 'urn:oasis:names:tc:SAML:2.0:logout:global-timeout'; + const LOGOUT_REASON_SP_TIMEOUT = 'urn:oasis:names:tc:SAML:2.0:logout:sp-timeout'; + + const XMLDSIG_DIGEST_MD5 = 'http://www.w3.org/2001/04/xmldsig-more#md5'; + + const ATTRIBUTE_NAME_FORMAT_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified'; + + /** + * @param string $value + * + * @return bool + */ + public static function isProtocolValid($value) + { + static $arr = array( + self::PROTOCOL_SAML2, + self::PROTOCOL_SAML1, + self::PROTOCOL_SAML11, + self::PROTOCOL_SHIB1, + self::PROTOCOL_WS_FED, + ); + + return in_array($value, $arr); + } + + /** + * @param string $value + * + * @return bool + */ + public static function isNsValid($value) + { + static $arr = array( + self::NS_PROTOCOL, + self::NS_METADATA, + self::NS_ASSERTION, + self::NS_XMLDSIG, + ); + + return in_array($value, $arr); + } + + /** + * @param string $value + * + * @return bool + */ + public static function isNameIdFormatValid($value) + { + static $arr = array( + self::NAME_ID_FORMAT_NONE, + self::NAME_ID_FORMAT_ENTITY, + self::NAME_ID_FORMAT_PERSISTENT, + self::NAME_ID_FORMAT_TRANSIENT, + self::NAME_ID_FORMAT_EMAIL, + self::NAME_ID_FORMAT_SHIB_NAME_ID, + self::NAME_ID_FORMAT_X509_SUBJECT_NAME, + self::NAME_ID_FORMAT_WINDOWS, + self::NAME_ID_FORMAT_KERBEROS, + self::NAME_ID_FORMAT_UNSPECIFIED, + ); + + return in_array($value, $arr); + } + + /** + * @param string $value + * + * @return bool + */ + public static function isBindingValid($value) + { + static $arr = array( + self::BINDING_SAML2_HTTP_REDIRECT, + self::BINDING_SAML2_HTTP_POST, + self::BINDING_SAML2_HTTP_ARTIFACT, + self::BINDING_SAML2_SOAP, + self::BINDING_SAML2_HTTP_POST_SIMPLE_SIGN, + self::BINDING_SHIB1_AUTHN_REQUEST, + self::BINDING_SAML1_BROWSER_POST, + self::BINDING_SAML1_ARTIFACT1, + self::BINDING_WS_FED_WEB_SVC, + ); + + return in_array($value, $arr); + } + + /** + * @param string $value + * + * @return bool + */ + public static function isStatusValid($value) + { + static $arr = array( + self::STATUS_SUCCESS, + self::STATUS_REQUESTER, + self::STATUS_RESPONDER, + self::STATUS_VERSION_MISMATCH, + self::STATUS_NO_PASSIVE, + self::STATUS_PARTIAL_LOGOUT, + self::STATUS_PROXY_COUNT_EXCEEDED, + self::STATUS_INVALID_NAME_ID_POLICY, + self::STATUS_UNSUPPORTED_BINDING, + ); + + return in_array($value, $arr); + } + + /** + * @param string $value + * + * @return bool + */ + public static function isConfirmationMethodValid($value) + { + static $arr = array( + self::CONFIRMATION_METHOD_BEARER, + self::CONFIRMATION_METHOD_HOK, + self::CONFIRMATION_METHOD_SENDER_VOUCHES, + ); + + return in_array($value, $arr); + } + + /** + * @param string $value + * + * @return bool + */ + public static function isAuthnContextValid($value) + { + static $arr = array( + self::AUTHN_CONTEXT_PASSWORD, + self::AUTHN_CONTEXT_UNSPECIFIED, + self::AUTHN_CONTEXT_PASSWORD_PROTECTED_TRANSPORT, + self::AUTHN_CONTEXT_WINDOWS, + ); + + return in_array($value, $arr); + } + + /** + * @param string $value + * + * @return bool + */ + public static function isLogoutReasonValid($value) + { + static $arr = array( + self::LOGOUT_REASON_USER, + self::LOGOUT_REASON_ADMIN, + self::LOGOUT_REASON_GLOBAL_TIMEOUT, + self::LOGOUT_REASON_SP_TIMEOUT, + ); + + return in_array($value, $arr); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/State/Request/RequestState.php b/vendor/lightsaml/lightsaml/src/LightSaml/State/Request/RequestState.php new file mode 100644 index 0000000000..d03b957818 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/State/Request/RequestState.php @@ -0,0 +1,116 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\State\Request; + +use LightSaml\Meta\ParameterBag; + +class RequestState implements \Serializable +{ + /** @var string */ + private $id; + + /** @var ParameterBag */ + private $parameters; + + /** + * @param string $id + * @param mixed $nonce + */ + public function __construct($id = null, $nonce = null) + { + $this->id = $id; + $this->parameters = new ParameterBag(); + if ($nonce) { + $this->parameters->set('nonce', $nonce); + } + } + + /** + * @param string $id + * + * @return RequestState + */ + public function setId($id) + { + $this->id = $id; + + return $this; + } + + /** + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * @return ParameterBag + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * @deprecated Since 1.2, to be removed in 2.0. Use getParameters() instead + * + * @param mixed $nonce + * + * @return RequestState + */ + public function setNonce($nonce) + { + $this->parameters->set('nonce', $nonce); + + return $this; + } + + /** + * @deprecated Since 1.2, to be removed in 2.0. Use getParameters() instead + * + * @return mixed + */ + public function getNonce() + { + return $this->parameters->get('nonce'); + } + + /** + * (PHP 5 >= 5.1.0)
+ * String representation of object. + * + * @see http://php.net/manual/en/serializable.serialize.php + * + * @return string the string representation of the object or null + */ + public function serialize() + { + $nonce = $this->getNonce(); + + return serialize(array($this->id, $nonce, $this->parameters->serialize())); + } + + /** + * @param string $serialized The string representation of the object + * + * @return void + */ + public function unserialize($serialized) + { + $nonce = null; + $this->parameters = new ParameterBag(); + list($this->id, $nonce, $parameters) = unserialize($serialized); + $this->parameters->unserialize($parameters); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/State/Request/RequestStateParameters.php b/vendor/lightsaml/lightsaml/src/LightSaml/State/Request/RequestStateParameters.php new file mode 100644 index 0000000000..3379e1a846 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/State/Request/RequestStateParameters.php @@ -0,0 +1,28 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\State\Request; + +final class RequestStateParameters +{ + const ID = 'id'; + const TYPE = 'type'; + const TIMESTAMP = 'ts'; + const PARTY = 'party'; + const RELAY_STATE = 'relay_state'; + const NAME_ID = 'name_id'; + const NAME_ID_FORMAT = 'name_id_format'; + const SESSION_INDEX = 'session_index'; + + private function __construct() + { + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/State/Sso/SsoSessionState.php b/vendor/lightsaml/lightsaml/src/LightSaml/State/Sso/SsoSessionState.php new file mode 100644 index 0000000000..33d079e0f2 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/State/Sso/SsoSessionState.php @@ -0,0 +1,343 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\State\Sso; + +use LightSaml\Error\LightSamlException; +use LightSaml\Meta\ParameterBag; + +class SsoSessionState implements \Serializable +{ + /** @var string */ + protected $idpEntityId; + + /** @var string */ + protected $spEntityId; + + /** @var string */ + protected $nameId; + + /** @var string */ + protected $nameIdFormat; + + /** @var string */ + protected $sessionIndex; + + /** @var \DateTime */ + protected $sessionInstant; + + /** @var \DateTime */ + protected $firstAuthOn; + + /** @var \DateTime */ + protected $lastAuthOn; + + /** @var ParameterBag */ + protected $parameters; + + public function __construct() + { + $this->parameters = new ParameterBag(); + } + + /** + * @return string + */ + public function getIdpEntityId() + { + return $this->idpEntityId; + } + + /** + * @param string $idpEntityId + * + * @return SsoSessionState + */ + public function setIdpEntityId($idpEntityId) + { + $this->idpEntityId = $idpEntityId; + + return $this; + } + + /** + * @return string + */ + public function getSpEntityId() + { + return $this->spEntityId; + } + + /** + * @param string $spEntityId + * + * @return SsoSessionState + */ + public function setSpEntityId($spEntityId) + { + $this->spEntityId = $spEntityId; + + return $this; + } + + /** + * @return string + */ + public function getNameId() + { + return $this->nameId; + } + + /** + * @param string $nameId + * + * @return SsoSessionState + */ + public function setNameId($nameId) + { + $this->nameId = $nameId; + + return $this; + } + + /** + * @return string + */ + public function getNameIdFormat() + { + return $this->nameIdFormat; + } + + /** + * @param string $nameIdFormat + * + * @return SsoSessionState + */ + public function setNameIdFormat($nameIdFormat) + { + $this->nameIdFormat = $nameIdFormat; + + return $this; + } + + /** + * @return string + */ + public function getSessionIndex() + { + return $this->sessionIndex; + } + + /** + * @param string $sessionIndex + * + * @return SsoSessionState + */ + public function setSessionIndex($sessionIndex) + { + $this->sessionIndex = $sessionIndex; + + return $this; + } + + /** + * @return \DateTime + */ + public function getFirstAuthOn() + { + return $this->firstAuthOn; + } + + /** + * @param \DateTime $firstAuthOn + * + * @return SsoSessionState + */ + public function setFirstAuthOn($firstAuthOn) + { + $this->firstAuthOn = $firstAuthOn; + + return $this; + } + + /** + * @return \DateTime + */ + public function getLastAuthOn() + { + return $this->lastAuthOn; + } + + /** + * @param \DateTime $lastAuthOn + * + * @return SsoSessionState + */ + public function setLastAuthOn($lastAuthOn) + { + $this->lastAuthOn = $lastAuthOn; + + return $this; + } + + /** + * @return \DateTime + */ + public function getSessionInstant() + { + return $this->sessionInstant; + } + + /** + * @param \DateTime $sessionInstant + * + * @return SsoSessionState + */ + public function setSessionInstant($sessionInstant) + { + $this->sessionInstant = $sessionInstant; + + return $this; + } + + /** + * @return ParameterBag + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * @deprecated Since 1.2, will be removed in 2.0. Use getParameters() instead + * + * @return array + */ + public function getOptions() + { + return $this->parameters->all(); + } + + /** + * @deprecated Since 1.2, will be removed in 2.0. Use getParameters() instead + * + * @param string $name + * @param mixed $value + * + * @return SsoSessionState + */ + public function addOption($name, $value) + { + $this->parameters->set($name, $value); + + return $this; + } + + /** + * @deprecated Since 1.2, will be removed in 2.0. Use getParameters() instead + * + * @param string $name + * + * @return SsoSessionState + */ + public function removeOption($name) + { + $this->parameters->remove($name); + + return $this; + } + + /** + * @deprecated Since 1.2, will be removed in 2.0. Use getParameters() instead + * + * @param string $name + * + * @return bool + */ + public function hasOption($name) + { + return $this->parameters->has($name); + } + + /** + * @param string $partyId + * + * @return string Other party id + * + * @throws \LightSaml\Error\LightSamlException If $partyId does not match sp or idp entity id + */ + public function getOtherPartyId($partyId) + { + if ($partyId == $this->idpEntityId) { + return $this->spEntityId; + } elseif ($partyId == $this->spEntityId) { + return $this->idpEntityId; + } + + throw new LightSamlException(sprintf( + 'Party "%s" is not included in sso session between "%s" and "%s"', + $partyId, + $this->idpEntityId, + $this->spEntityId + )); + } + + /** + * @return string the string representation of the object or null + */ + public function serialize() + { + return serialize(array( + $this->idpEntityId, + $this->spEntityId, + $this->nameId, + $this->nameIdFormat, + $this->sessionIndex, + $this->sessionInstant, + $this->firstAuthOn, + $this->lastAuthOn, + [], + $this->parameters, + )); + } + + /** + * @param string $serialized + * + * @return void + */ + public function unserialize($serialized) + { + $data = unserialize($serialized); + + // add a few extra elements in the array to ensure that we have enough keys when unserializing + // older data which does not include all properties. + $data = array_merge($data, array_fill(0, 5, null)); + + list( + $this->idpEntityId, + $this->spEntityId, + $this->nameId, + $this->nameIdFormat, + $this->sessionIndex, + $this->sessionInstant, + $this->firstAuthOn, + $this->lastAuthOn, + $options, + $this->parameters + ) = $data; + + // if deserialized from old format, set old options to new parameters + if ($options && 0 == $this->parameters->count()) { + $this->parameters->replace($options); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/State/Sso/SsoState.php b/vendor/lightsaml/lightsaml/src/LightSaml/State/Sso/SsoState.php new file mode 100644 index 0000000000..45adb3902c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/State/Sso/SsoState.php @@ -0,0 +1,223 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\State\Sso; + +use LightSaml\Meta\ParameterBag; + +class SsoState implements \Serializable +{ + /** @var string */ + private $localSessionId; + + /** @var ParameterBag */ + private $parameters; + + /** @var SsoSessionState[] */ + private $ssoSessions = array(); + + public function __construct() + { + $this->parameters = new ParameterBag(); + } + + /** + * @return string + */ + public function getLocalSessionId() + { + return $this->localSessionId; + } + + /** + * @param string $localSessionId + * + * @return SsoState + */ + public function setLocalSessionId($localSessionId) + { + $this->localSessionId = $localSessionId; + + return $this; + } + + /** + * @return ParameterBag + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * @deprecated Since 1.2, to be removed in 2.0. Use getParameters() instead + * + * @return array + */ + public function getOptions() + { + return $this->parameters->all(); + } + + /** + * @deprecated Since 1.2, to be removed in 2.0. Use getParameters() instead + * + * @param string $name + * @param mixed $value + * + * @return SsoState + */ + public function addOption($name, $value) + { + $this->parameters->set($name, $value); + + return $this; + } + + /** + * @deprecated Since 1.2, to be removed in 2.0. Use getParameters() instead + * + * @param string $name + * + * @return SsoState + */ + public function removeOption($name) + { + $this->parameters->remove($name); + + return $this; + } + + /** + * @deprecated Since 1.2, to be removed in 2.0. Use getParameters() instead + * + * @param string $name + * + * @return bool + */ + public function hasOption($name) + { + return $this->parameters->has($name); + } + + /** + * @return SsoSessionState[] + */ + public function getSsoSessions() + { + return $this->ssoSessions; + } + + /** + * @param SsoSessionState[] $ssoSessions + * + * @return SsoState + */ + public function setSsoSessions(array $ssoSessions) + { + $this->ssoSessions = array(); + foreach ($ssoSessions as $ssoSession) { + $this->addSsoSession($ssoSession); + } + + return $this; + } + + /** + * @param SsoSessionState $ssoSessionState + * + * @return SsoState + */ + public function addSsoSession(SsoSessionState $ssoSessionState) + { + $this->ssoSessions[] = $ssoSessionState; + + return $this; + } + + /** + * @param $idpEntityId + * @param $spEntityId + * @param $nameId + * @param $nameIdFormat + * @param $sessionIndex + * + * @return SsoSessionState[] + */ + public function filter($idpEntityId, $spEntityId, $nameId, $nameIdFormat, $sessionIndex) + { + $result = array(); + + foreach ($this->ssoSessions as $ssoSession) { + if ((!$idpEntityId || $ssoSession->getIdpEntityId() === $idpEntityId) && + (!$spEntityId || $ssoSession->getSpEntityId() === $spEntityId) && + (!$nameId || $ssoSession->getNameId() === $nameId) && + (!$nameIdFormat || $ssoSession->getNameIdFormat() === $nameIdFormat) && + (!$sessionIndex || $ssoSession->getSessionIndex() === $sessionIndex) + ) { + $result[] = $ssoSession; + } + } + + return $result; + } + + /** + * @param callable $callback + * + * @return SsoState + */ + public function modify($callback) + { + $this->ssoSessions = array_values(array_filter($this->ssoSessions, $callback)); + + return $this; + } + + /** + * @return string the string representation of the object or null + */ + public function serialize() + { + return serialize(array( + $this->localSessionId, + $this->ssoSessions, + [], + $this->parameters, + )); + } + + /** + * @param string $serialized + * + * @return void + */ + public function unserialize($serialized) + { + $data = unserialize($serialized); + + // add a few extra elements in the array to ensure that we have enough keys when unserializing + // older data which does not include all properties. + $data = array_merge($data, array_fill(0, 5, null)); + $oldOptions = null; + + list( + $this->localSessionId, + $this->ssoSessions, + $oldOptions, // old deprecated options + $this->parameters) = $data; + + // in case it was serialized in old way, copy old options to parameters + if ($oldOptions && 0 == $this->parameters->count()) { + $this->parameters->add($oldOptions); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/CompositeCredentialStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/CompositeCredentialStore.php new file mode 100644 index 0000000000..a73d28f6bf --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/CompositeCredentialStore.php @@ -0,0 +1,47 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\Credential; + +use LightSaml\Credential\CredentialInterface; + +class CompositeCredentialStore implements CredentialStoreInterface +{ + /** @var CredentialStoreInterface[] */ + protected $stores = array(); + + /** + * @param string $entityId + * + * @return CredentialInterface[] + */ + public function getByEntityId($entityId) + { + $result = array(); + foreach ($this->stores as $store) { + $result = array_merge($result, $store->getByEntityId($entityId)); + } + + return $result; + } + + /** + * @param CredentialStoreInterface $store + * + * @return CompositeCredentialStore + */ + public function add(CredentialStoreInterface $store) + { + $this->stores[] = $store; + + return $this; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/CredentialStoreInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/CredentialStoreInterface.php new file mode 100644 index 0000000000..81632c2793 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/CredentialStoreInterface.php @@ -0,0 +1,24 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\Credential; + +use LightSaml\Credential\CredentialInterface; + +interface CredentialStoreInterface +{ + /** + * @param string $entityId + * + * @return CredentialInterface[] + */ + public function getByEntityId($entityId); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/Factory/CredentialFactory.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/Factory/CredentialFactory.php new file mode 100644 index 0000000000..d4194e4ebe --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/Factory/CredentialFactory.php @@ -0,0 +1,104 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\Credential\Factory; + +use LightSaml\Credential\CredentialInterface; +use LightSaml\Error\LightSamlBuildException; +use LightSaml\Store\Credential\CompositeCredentialStore; +use LightSaml\Store\Credential\CredentialStoreInterface; +use LightSaml\Store\Credential\MetadataCredentialStore; +use LightSaml\Store\Credential\StaticCredentialStore; +use LightSaml\Store\EntityDescriptor\EntityDescriptorStoreInterface; + +class CredentialFactory +{ + /** @var CredentialInterface[] */ + private $extraCredentials = []; + + /** + * @param CredentialInterface $credential + * + * @return CredentialFactory + */ + public function addExtraCredential(CredentialInterface $credential) + { + $this->extraCredentials[] = $credential; + + return $this; + } + + /** + * @param EntityDescriptorStoreInterface $idpEntityDescriptorStore + * @param EntityDescriptorStoreInterface $spEntityDescriptorStore + * @param string $ownEntityId + * @param CredentialStoreInterface $ownCredentialStore + * @param CredentialInterface[] $extraCredentials + * + * @return CompositeCredentialStore + */ + public function buildFromOwnCredentialStore( + EntityDescriptorStoreInterface $idpEntityDescriptorStore, + EntityDescriptorStoreInterface $spEntityDescriptorStore, + $ownEntityId, + CredentialStoreInterface $ownCredentialStore, + array $extraCredentials = null + ) { + return $this->build( + $idpEntityDescriptorStore, + $spEntityDescriptorStore, + $ownCredentialStore->getByEntityId($ownEntityId), + $extraCredentials + ); + } + + /** + * @param EntityDescriptorStoreInterface $idpEntityDescriptorStore + * @param EntityDescriptorStoreInterface $spEntityDescriptorStore + * @param CredentialInterface[] $ownCredentials + * @param CredentialInterface[] $extraCredentials + * + * @return CompositeCredentialStore + */ + public function build( + EntityDescriptorStoreInterface $idpEntityDescriptorStore, + EntityDescriptorStoreInterface $spEntityDescriptorStore, + array $ownCredentials, + array $extraCredentials = null + ) { + if (empty($ownCredentials)) { + throw new LightSamlBuildException('There are no own credentials'); + } + + $store = new CompositeCredentialStore(); + $store->add(new MetadataCredentialStore($idpEntityDescriptorStore)); + $store->add(new MetadataCredentialStore($spEntityDescriptorStore)); + + $ownCredentialsStore = new StaticCredentialStore(); + foreach ($ownCredentials as $credential) { + $ownCredentialsStore->add($credential); + } + $store->add($ownCredentialsStore); + + $extraCredentialsStore = new StaticCredentialStore(); + $store->add($extraCredentialsStore); + foreach ($this->extraCredentials as $credential) { + $extraCredentialsStore->add($credential); + } + if ($extraCredentials) { + foreach ($extraCredentials as $credential) { + $extraCredentialsStore->add($credential); + } + } + + return $store; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/MetadataCredentialStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/MetadataCredentialStore.php new file mode 100644 index 0000000000..469f2d7c34 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/MetadataCredentialStore.php @@ -0,0 +1,89 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\Credential; + +use LightSaml\Store\EntityDescriptor\EntityDescriptorStoreInterface; +use LightSaml\Model\Metadata\EntityDescriptor; +use LightSaml\Model\Metadata\SSODescriptor; +use LightSaml\Credential\Context\CredentialContextSet; +use LightSaml\Credential\Context\MetadataCredentialContext; +use LightSaml\Credential\CredentialInterface; +use LightSaml\Credential\X509Credential; + +class MetadataCredentialStore implements CredentialStoreInterface +{ + /** @var EntityDescriptorStoreInterface */ + protected $entityDescriptorProvider; + + /** + * @param EntityDescriptorStoreInterface $entityDescriptorProvider + */ + public function __construct(EntityDescriptorStoreInterface $entityDescriptorProvider) + { + $this->entityDescriptorProvider = $entityDescriptorProvider; + } + + /** + * @param string $entityId + * + * @return CredentialInterface[] + */ + public function getByEntityId($entityId) + { + $entityDescriptor = $this->entityDescriptorProvider->get($entityId); + if (false == $entityDescriptor) { + return array(); + } + + return $this->extractCredentials($entityDescriptor); + } + + /** + * @param EntityDescriptor $entityDescriptor + * + * @return CredentialInterface[] + */ + protected function extractCredentials(EntityDescriptor $entityDescriptor) + { + $result = array(); + + foreach ($entityDescriptor->getAllIdpSsoDescriptors() as $idpDescriptor) { + $this->handleDescriptor($idpDescriptor, $entityDescriptor, $result); + } + foreach ($entityDescriptor->getAllSpSsoDescriptors() as $spDescriptor) { + $this->handleDescriptor($spDescriptor, $entityDescriptor, $result); + } + + return $result; + } + + /** + * @param SSODescriptor $ssoDescriptor + * @param EntityDescriptor $entityDescriptor + * @param array $result + */ + protected function handleDescriptor(SSODescriptor $ssoDescriptor, EntityDescriptor $entityDescriptor, array &$result) + { + foreach ($ssoDescriptor->getAllKeyDescriptors() as $keyDescriptor) { + $credential = (new X509Credential($keyDescriptor->getCertificate())) + ->setEntityId($entityDescriptor->getEntityID()) + ->addKeyName($keyDescriptor->getCertificate()->getName()) + ->setCredentialContext(new CredentialContextSet(array( + new MetadataCredentialContext($keyDescriptor, $ssoDescriptor, $entityDescriptor), + ))) + ->setUsageType($keyDescriptor->getUse()) + ; + + $result[] = $credential; + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/StaticCredentialStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/StaticCredentialStore.php new file mode 100644 index 0000000000..a5fc9f182e --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/StaticCredentialStore.php @@ -0,0 +1,70 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\Credential; + +use LightSaml\Credential\CredentialInterface; + +class StaticCredentialStore implements CredentialStoreInterface +{ + /** + * entityID => CredentialInterface[]. + * + * @var array + */ + protected $credentials = array(); + + /** + * @param string $entityId + * + * @return CredentialInterface[] + */ + public function getByEntityId($entityId) + { + $this->checkEntityIdExistence($entityId); + + return $this->credentials[$entityId]; + } + + /** + * @param string $entityId + * + * @return bool + */ + public function has($entityId) + { + return array_key_exists($entityId, $this->credentials); + } + + /** + * @param CredentialInterface $credential + * + * @return StaticCredentialStore + */ + public function add(CredentialInterface $credential) + { + $this->checkEntityIdExistence($credential->getEntityId()); + + $this->credentials[$credential->getEntityId()][] = $credential; + + return $this; + } + + /** + * @param string $entityId + */ + private function checkEntityIdExistence($entityId) + { + if (false == $this->has($entityId)) { + $this->credentials[$entityId] = array(); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/X509FileCredentialStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/X509FileCredentialStore.php new file mode 100644 index 0000000000..beeafb8f8f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Credential/X509FileCredentialStore.php @@ -0,0 +1,72 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\Credential; + +use LightSaml\Credential\CredentialInterface; +use LightSaml\Credential\X509Credential; +use LightSaml\Credential\KeyHelper; +use LightSaml\Credential\X509Certificate; + +class X509FileCredentialStore implements CredentialStoreInterface +{ + /** @var string */ + private $entityId; + + /** @var string */ + private $certificatePath; + + /** @var string */ + private $keyPath; + + /** @var string */ + private $password; + + /** @var X509Credential */ + private $credential; + + /** + * @param string $entityId + * @param string $certificatePath + * @param string $keyPath + * @param string $password + */ + public function __construct($entityId, $certificatePath, $keyPath, $password) + { + $this->entityId = $entityId; + $this->certificatePath = $certificatePath; + $this->keyPath = $keyPath; + $this->password = $password; + } + + /** + * @param string $entityId + * + * @return CredentialInterface[] + */ + public function getByEntityId($entityId) + { + if ($entityId != $this->entityId) { + return []; + } + + if (null == $this->credential) { + $certificate = X509Certificate::fromFile($this->certificatePath); + $this->credential = new X509Credential( + $certificate, + KeyHelper::createPrivateKey($this->keyPath, $this->password, true, $certificate->getSignatureAlgorithm()) + ); + $this->credential->setEntityId($this->entityId); + } + + return [$this->credential]; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/EntityDescriptor/CompositeEntityDescriptorStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/EntityDescriptor/CompositeEntityDescriptorStore.php new file mode 100644 index 0000000000..efb569b54c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/EntityDescriptor/CompositeEntityDescriptorStore.php @@ -0,0 +1,88 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\EntityDescriptor; + +use LightSaml\Model\Metadata\EntityDescriptor; + +class CompositeEntityDescriptorStore implements EntityDescriptorStoreInterface +{ + /** @var EntityDescriptorStoreInterface[] */ + private $children = []; + + /** + * @param EntityDescriptorStoreInterface[] $stores + */ + public function __construct(array $stores = array()) + { + foreach ($stores as $store) { + $this->add($store); + } + } + + /** + * @param EntityDescriptorStoreInterface $store + * + * @return CompositeEntityDescriptorStore This instance + */ + public function add(EntityDescriptorStoreInterface $store) + { + $this->children[] = $store; + + return $this; + } + + /** + * @param string $entityId + * + * @return EntityDescriptor|null + */ + public function get($entityId) + { + foreach ($this->children as $store) { + $result = $store->get($entityId); + if ($result) { + return $result; + } + } + + return null; + } + + /** + * @param string $entityId + * + * @return bool + */ + public function has($entityId) + { + foreach ($this->children as $store) { + if ($store->has($entityId)) { + return true; + } + } + + return false; + } + + /** + * @return array|EntityDescriptor[] + */ + public function all() + { + $result = array(); + foreach ($this->children as $store) { + $result = array_merge($result, $store->all()); + } + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/EntityDescriptor/EntityDescriptorStoreInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/EntityDescriptor/EntityDescriptorStoreInterface.php new file mode 100644 index 0000000000..23ee2896d4 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/EntityDescriptor/EntityDescriptorStoreInterface.php @@ -0,0 +1,36 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\EntityDescriptor; + +use LightSaml\Model\Metadata\EntityDescriptor; + +interface EntityDescriptorStoreInterface +{ + /** + * @param string $entityId + * + * @return EntityDescriptor|null + */ + public function get($entityId); + + /** + * @param string $entityId + * + * @return bool + */ + public function has($entityId); + + /** + * @return array|EntityDescriptor[] + */ + public function all(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/EntityDescriptor/FileEntityDescriptorStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/EntityDescriptor/FileEntityDescriptorStore.php new file mode 100644 index 0000000000..c83e5857d7 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/EntityDescriptor/FileEntityDescriptorStore.php @@ -0,0 +1,89 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\EntityDescriptor; + +use LightSaml\Error\LightSamlXmlException; +use LightSaml\Model\Metadata\EntitiesDescriptor; +use LightSaml\Model\Metadata\EntityDescriptor; + +class FileEntityDescriptorStore implements EntityDescriptorStoreInterface +{ + private $filename; + + /** @var EntityDescriptor|EntitiesDescriptor */ + private $object; + + /** + * @param string $filename + */ + public function __construct($filename) + { + $this->filename = $filename; + } + + /** + * @param string $entityId + * + * @return EntityDescriptor|null + */ + public function get($entityId) + { + if (null == $this->object) { + $this->load(); + } + + if ($this->object instanceof EntityDescriptor) { + if ($this->object->getEntityID() == $entityId) { + return $this->object; + } else { + return null; + } + } else { + return $this->object->getByEntityId($entityId); + } + } + + /** + * @param string $entityId + * + * @return bool + */ + public function has($entityId) + { + return null != $this->get($entityId); + } + + /** + * @return array|EntityDescriptor[] + */ + public function all() + { + if (null == $this->object) { + $this->load(); + } + + if ($this->object instanceof EntityDescriptor) { + return [$this->object]; + } else { + return $this->object->getAllEntityDescriptors(); + } + } + + private function load() + { + try { + $this->object = EntityDescriptor::load($this->filename); + } catch (LightSamlXmlException $ex) { + $this->object = EntitiesDescriptor::load($this->filename); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/EntityDescriptor/FixedEntityDescriptorStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/EntityDescriptor/FixedEntityDescriptorStore.php new file mode 100644 index 0000000000..c9a4e1a913 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/EntityDescriptor/FixedEntityDescriptorStore.php @@ -0,0 +1,78 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\EntityDescriptor; + +use LightSaml\Model\Metadata\EntitiesDescriptor; +use LightSaml\Model\Metadata\EntityDescriptor; + +class FixedEntityDescriptorStore implements EntityDescriptorStoreInterface +{ + /** @var array|EntityDescriptor[] entityId=>descriptor */ + protected $descriptors = array(); + + /** + * @param EntityDescriptor|EntitiesDescriptor $entityDescriptor + * + * @return FixedEntityDescriptorStore + * + * @throws \InvalidArgumentException + */ + public function add($entityDescriptor) + { + if ($entityDescriptor instanceof EntityDescriptor) { + if (false == $entityDescriptor->getEntityID()) { + throw new \InvalidArgumentException('EntityDescriptor must have entityId set'); + } + $this->descriptors[$entityDescriptor->getEntityID()] = $entityDescriptor; + } elseif ($entityDescriptor instanceof EntitiesDescriptor) { + foreach ($entityDescriptor->getAllItems() as $item) { + $this->add($item); + } + } else { + throw new \InvalidArgumentException('Expected EntityDescriptor or EntitiesDescriptor'); + } + + return $this; + } + + /** + * @param string $entityId + * + * @return EntityDescriptor|null + */ + public function get($entityId) + { + if (isset($this->descriptors[$entityId])) { + return $this->descriptors[$entityId]; + } + + return null; + } + + /** + * @param string $entityId + * + * @return bool + */ + public function has($entityId) + { + return isset($this->descriptors[$entityId]); + } + + /** + * @return array|EntityDescriptor[] + */ + public function all() + { + return array_values($this->descriptors); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/Id/IdArrayStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Id/IdArrayStore.php new file mode 100644 index 0000000000..4b2b98141b --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Id/IdArrayStore.php @@ -0,0 +1,44 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\Id; + +class IdArrayStore implements IdStoreInterface +{ + /** @var array */ + protected $store = array(); + + /** + * @param string $entityId + * @param string $id + * @param \DateTime $expiryTime + * + * @return void + */ + public function set($entityId, $id, \DateTime $expiryTime) + { + if (false == isset($this->store[$entityId])) { + $this->store[$entityId] = array(); + } + $this->store[$entityId][$id] = $expiryTime; + } + + /** + * @param string $entityId + * @param string $id + * + * @return bool + */ + public function has($entityId, $id) + { + return isset($this->store[$entityId][$id]); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/Id/IdStoreInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Id/IdStoreInterface.php new file mode 100644 index 0000000000..943adccf34 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Id/IdStoreInterface.php @@ -0,0 +1,32 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\Id; + +interface IdStoreInterface +{ + /** + * @param string $entityId + * @param string $id + * @param \DateTime $expiryTime + * + * @return void + */ + public function set($entityId, $id, \DateTime $expiryTime); + + /** + * @param string $entityId + * @param string $id + * + * @return bool + */ + public function has($entityId, $id); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/Id/NullIdStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Id/NullIdStore.php new file mode 100644 index 0000000000..fc111f9206 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Id/NullIdStore.php @@ -0,0 +1,37 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\Id; + +class NullIdStore implements IdStoreInterface +{ + /** + * @param string $entityId + * @param string $id + * @param \DateTime $expiryTime + * + * @return void + */ + public function set($entityId, $id, \DateTime $expiryTime) + { + } + + /** + * @param string $entityId + * @param string $id + * + * @return bool + */ + public function has($entityId, $id) + { + return false; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/Request/AbstractRequestStateArrayStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Request/AbstractRequestStateArrayStore.php new file mode 100644 index 0000000000..6b1e912048 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Request/AbstractRequestStateArrayStore.php @@ -0,0 +1,89 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\Request; + +use LightSaml\State\Request\RequestState; + +abstract class AbstractRequestStateArrayStore implements RequestStateStoreInterface +{ + /** + * @param RequestState $state + * + * @return AbstractRequestStateArrayStore + */ + public function set(RequestState $state) + { + $arr = $this->getArray(); + $arr[$state->getId()] = $state; + $this->setArray($arr); + + return $this; + } + + /** + * @param string $id + * + * @return RequestState|null + */ + public function get($id) + { + $result = null; + $arr = $this->getArray(); + if (false == is_array($arr)) { + $arr = array(); + $this->setArray($arr); + } + if (isset($arr[$id])) { + $result = $arr[$id]; + } + if ($result instanceof RequestState) { + return $result; + } + + return null; + } + + /** + * @param string $id + * + * @return bool + */ + public function remove($id) + { + $arr = $this->getArray(); + $result = isset($arr[$id]); + unset($arr[$id]); + $this->setArray($arr); + + return $result; + } + + /** + * @return void + */ + public function clear() + { + $this->setArray(array()); + } + + /** + * @return array + */ + abstract protected function getArray(); + + /** + * @param array $arr + * + * @return AbstractRequestStateArrayStore + */ + abstract protected function setArray(array $arr); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/Request/RequestStateArrayStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Request/RequestStateArrayStore.php new file mode 100644 index 0000000000..005988b35f --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Request/RequestStateArrayStore.php @@ -0,0 +1,35 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\Request; + +class RequestStateArrayStore extends AbstractRequestStateArrayStore +{ + private $arrayStore = array(); + + /** + * @return array + */ + protected function getArray() + { + return $this->arrayStore; + } + + /** + * @param array $arr + * + * @return AbstractRequestStateArrayStore + */ + protected function setArray(array $arr) + { + $this->arrayStore = $arr; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/Request/RequestStateSessionStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Request/RequestStateSessionStore.php new file mode 100644 index 0000000000..6fa5c018e6 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Request/RequestStateSessionStore.php @@ -0,0 +1,64 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\Request; + +use Symfony\Component\HttpFoundation\Session\SessionInterface; + +class RequestStateSessionStore extends AbstractRequestStateArrayStore +{ + /** @var \Symfony\Component\HttpFoundation\Session\SessionInterface */ + protected $session; + + /** @var string */ + protected $providerId; + + /** @var string */ + protected $prefix; + + /** + * @param SessionInterface $session + * @param string $providerId + * @param string $prefix + */ + public function __construct(SessionInterface $session, $providerId, $prefix = 'saml_request_state_') + { + $this->session = $session; + $this->providerId = $providerId; + $this->prefix = $prefix; + } + + /** + * @return string + */ + protected function getKey() + { + return sprintf('%s_%s', $this->providerId, $this->prefix); + } + + /** + * @return array + */ + protected function getArray() + { + return $this->session->get($this->getKey(), array()); + } + + /** + * @param array $arr + * + * @return AbstractRequestStateArrayStore + */ + protected function setArray(array $arr) + { + $this->session->set($this->getKey(), $arr); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/Request/RequestStateStoreInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Request/RequestStateStoreInterface.php new file mode 100644 index 0000000000..c121b0c3d5 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Request/RequestStateStoreInterface.php @@ -0,0 +1,43 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\Request; + +use LightSaml\State\Request\RequestState; + +interface RequestStateStoreInterface +{ + /** + * @param RequestState $state + * + * @return RequestStateStoreInterface + */ + public function set(RequestState $state); + + /** + * @param string $id + * + * @return RequestState|null + */ + public function get($id); + + /** + * @param string $id + * + * @return bool + */ + public function remove($id); + + /** + * @return void + */ + public function clear(); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/Sso/SsoStateFixedStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Sso/SsoStateFixedStore.php new file mode 100644 index 0000000000..51fd9bb590 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Sso/SsoStateFixedStore.php @@ -0,0 +1,42 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\Sso; + +use LightSaml\State\Sso\SsoState; + +class SsoStateFixedStore implements SsoStateStoreInterface +{ + /** @var SsoState */ + protected $ssoState; + + /** + * @return SsoState + */ + public function get() + { + if (null == $this->ssoState) { + $this->ssoState = new SsoState(); + } + + return $this->ssoState; + } + + /** + * @param SsoState $ssoState + * + * @return void + */ + public function set(SsoState $ssoState) + { + $this->ssoState = $ssoState; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/Sso/SsoStateSessionStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Sso/SsoStateSessionStore.php new file mode 100644 index 0000000000..3ac5195d31 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Sso/SsoStateSessionStore.php @@ -0,0 +1,59 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\Sso; + +use LightSaml\State\Sso\SsoState; +use Symfony\Component\HttpFoundation\Session\SessionInterface; + +class SsoStateSessionStore implements SsoStateStoreInterface +{ + /** @var SessionInterface */ + protected $session; + + /** @var string */ + protected $key; + + /** + * @param SessionInterface $session + * @param string $key + */ + public function __construct(SessionInterface $session, $key) + { + $this->session = $session; + $this->key = $key; + } + + /** + * @return SsoState + */ + public function get() + { + $result = $this->session->get($this->key); + if (null == $result) { + $result = new SsoState(); + $this->set($result); + } + + return $result; + } + + /** + * @param SsoState $ssoState + * + * @return void + */ + public function set(SsoState $ssoState) + { + $ssoState->setLocalSessionId($this->session->getId()); + $this->session->set($this->key, $ssoState); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/Sso/SsoStateStoreInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Sso/SsoStateStoreInterface.php new file mode 100644 index 0000000000..9e6db6a4ba --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/Sso/SsoStateStoreInterface.php @@ -0,0 +1,29 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\Sso; + +use LightSaml\State\Sso\SsoState; + +interface SsoStateStoreInterface +{ + /** + * @return SsoState + */ + public function get(); + + /** + * @param SsoState $ssoState + * + * @return void + */ + public function set(SsoState $ssoState); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/TrustOptions/CompositeTrustOptionsStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/TrustOptions/CompositeTrustOptionsStore.php new file mode 100644 index 0000000000..f78087f478 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/TrustOptions/CompositeTrustOptionsStore.php @@ -0,0 +1,75 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\TrustOptions; + +use LightSaml\Meta\TrustOptions\TrustOptions; + +class CompositeTrustOptionsStore implements TrustOptionsStoreInterface +{ + /** @var TrustOptionsStoreInterface[] */ + private $children = []; + + /** + * @param TrustOptionsStoreInterface[] $stores + */ + public function __construct(array $stores = array()) + { + foreach ($stores as $store) { + $this->add($store); + } + } + + /** + * @param TrustOptionsStoreInterface $store + * + * @return CompositeTrustOptionsStore This instance + */ + public function add(TrustOptionsStoreInterface $store) + { + $this->children[] = $store; + + return $this; + } + + /** + * @param string $entityId + * + * @return TrustOptions|null + */ + public function get($entityId) + { + foreach ($this->children as $store) { + $result = $store->get($entityId); + if ($result) { + return $result; + } + } + + return null; + } + + /** + * @param string $entityId + * + * @return bool + */ + public function has($entityId) + { + foreach ($this->children as $store) { + if ($store->has($entityId)) { + return true; + } + } + + return false; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/TrustOptions/FixedTrustOptionsStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/TrustOptions/FixedTrustOptionsStore.php new file mode 100644 index 0000000000..1e414445be --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/TrustOptions/FixedTrustOptionsStore.php @@ -0,0 +1,60 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\TrustOptions; + +use LightSaml\Meta\TrustOptions\TrustOptions; + +class FixedTrustOptionsStore implements TrustOptionsStoreInterface +{ + /** @var TrustOptions */ + protected $option; + + /** + * @param TrustOptions $option + */ + public function __construct(TrustOptions $option = null) + { + $this->option = $option; + } + + /** + * @param TrustOptions|null $trustOptions + * + * @return FixedTrustOptionsStore + */ + public function setTrustOptions(TrustOptions $trustOptions = null) + { + $this->option = $trustOptions; + + return $this; + } + + /** + * @param string $entityId + * + * @return TrustOptions|null + */ + public function get($entityId) + { + return $this->option; + } + + /** + * @param string $entityId + * + * @return bool + */ + public function has($entityId) + { + return true; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/TrustOptions/StaticTrustOptionsStore.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/TrustOptions/StaticTrustOptionsStore.php new file mode 100644 index 0000000000..ac4406211c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/TrustOptions/StaticTrustOptionsStore.php @@ -0,0 +1,53 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\TrustOptions; + +use LightSaml\Meta\TrustOptions\TrustOptions; + +class StaticTrustOptionsStore implements TrustOptionsStoreInterface +{ + /** @var TrustOptions[] */ + protected $options = array(); + + /** + * @param string $entityId + * @param TrustOptions $options + * + * @return StaticTrustOptionsStore + */ + public function add($entityId, TrustOptions $options) + { + $this->options[$entityId] = $options; + + return $this; + } + + /** + * @param string $entityId + * + * @return TrustOptions|null + */ + public function get($entityId) + { + return isset($this->options[$entityId]) ? $this->options[$entityId] : null; + } + + /** + * @param string $entityId + * + * @return bool + */ + public function has($entityId) + { + return isset($this->options[$entityId]); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Store/TrustOptions/TrustOptionsStoreInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Store/TrustOptions/TrustOptionsStoreInterface.php new file mode 100644 index 0000000000..38cc2cf85a --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Store/TrustOptions/TrustOptionsStoreInterface.php @@ -0,0 +1,31 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Store\TrustOptions; + +use LightSaml\Meta\TrustOptions\TrustOptions; + +interface TrustOptionsStoreInterface +{ + /** + * @param string $entityId + * + * @return TrustOptions|null + */ + public function get($entityId); + + /** + * @param string $entityId + * + * @return bool + */ + public function has($entityId); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Assertion/AssertionTimeValidator.php b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Assertion/AssertionTimeValidator.php new file mode 100644 index 0000000000..90273cd78b --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Assertion/AssertionTimeValidator.php @@ -0,0 +1,101 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Validator\Model\Assertion; + +use LightSaml\Error\LightSamlValidationException; +use LightSaml\Helper; +use LightSaml\Model\Assertion\Assertion; + +class AssertionTimeValidator implements AssertionTimeValidatorInterface +{ + /** + * @param Assertion $assertion + * @param int $now + * @param int $allowedSecondsSkew + * + * @throws \LightSaml\Error\LightSamlValidationException + * + * @return void + */ + public function validateTimeRestrictions(Assertion $assertion, $now, $allowedSecondsSkew) + { + if ($allowedSecondsSkew < 0) { + $allowedSecondsSkew = -1 * $allowedSecondsSkew; + } + + $this->validateConditions($assertion, $now, $allowedSecondsSkew); + $this->validateAuthnStatements($assertion, $now, $allowedSecondsSkew); + $this->validateSubject($assertion, $now, $allowedSecondsSkew); + } + + /** + * @param Assertion $assertion + * @param int $now + * @param int $allowedSecondsSkew + */ + protected function validateConditions(Assertion $assertion, $now, $allowedSecondsSkew) + { + if (false == $assertion->getConditions()) { + return; + } + + if (false == Helper::validateNotBefore($assertion->getConditions()->getNotBeforeTimestamp(), $now, $allowedSecondsSkew)) { + throw new LightSamlValidationException('Conditions.NotBefore must not be in the future'); + } + + if (false == Helper::validateNotOnOrAfter($assertion->getConditions()->getNotOnOrAfterTimestamp(), $now, $allowedSecondsSkew)) { + throw new LightSamlValidationException('Conditions.NotOnOrAfter must not be in the past'); + } + } + + /** + * @param Assertion $assertion + * @param int $now + * @param int $allowedSecondsSkew + */ + protected function validateAuthnStatements(Assertion $assertion, $now, $allowedSecondsSkew) + { + if (false == $assertion->getAllAuthnStatements()) { + return; + } + + foreach ($assertion->getAllAuthnStatements() as $authnStatement) { + if (false == Helper::validateNotOnOrAfter($authnStatement->getSessionNotOnOrAfterTimestamp(), $now, $allowedSecondsSkew)) { + throw new LightSamlValidationException('AuthnStatement attribute SessionNotOnOrAfter MUST be in the future'); + } + // TODO: Consider validating that authnStatement.AuthnInstant is in the past + } + } + + /** + * @param Assertion $assertion + * @param int $now + * @param int $allowedSecondsSkew + */ + protected function validateSubject(Assertion $assertion, $now, $allowedSecondsSkew) + { + if (false == $assertion->getSubject()) { + return; + } + + foreach ($assertion->getSubject()->getAllSubjectConfirmations() as $subjectConfirmation) { + if ($subjectConfirmation->getSubjectConfirmationData()) { + if (false == Helper::validateNotBefore($subjectConfirmation->getSubjectConfirmationData()->getNotBeforeTimestamp(), $now, $allowedSecondsSkew)) { + throw new LightSamlValidationException('SubjectConfirmationData.NotBefore must not be in the future'); + } + if (false == Helper::validateNotOnOrAfter($subjectConfirmation->getSubjectConfirmationData()->getNotOnOrAfterTimestamp(), $now, $allowedSecondsSkew)) { + throw new LightSamlValidationException('SubjectConfirmationData.NotOnOrAfter must not be in the past'); + } + } + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Assertion/AssertionTimeValidatorInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Assertion/AssertionTimeValidatorInterface.php new file mode 100644 index 0000000000..fc5965c809 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Assertion/AssertionTimeValidatorInterface.php @@ -0,0 +1,28 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Validator\Model\Assertion; + +use LightSaml\Model\Assertion\Assertion; + +interface AssertionTimeValidatorInterface +{ + /** + * @param Assertion $assertion + * @param int $now + * @param int $allowedSecondsSkew + * + * @throws \LightSaml\Error\LightSamlValidationException + * + * @return void + */ + public function validateTimeRestrictions(Assertion $assertion, $now, $allowedSecondsSkew); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Assertion/AssertionValidator.php b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Assertion/AssertionValidator.php new file mode 100644 index 0000000000..03b7a4674b --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Assertion/AssertionValidator.php @@ -0,0 +1,206 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Validator\Model\Assertion; + +use LightSaml\Error\LightSamlValidationException; +use LightSaml\Helper; +use LightSaml\Model\Assertion\Assertion; +use LightSaml\Model\Assertion\AttributeStatement; +use LightSaml\Model\Assertion\AudienceRestriction; +use LightSaml\Model\Assertion\AuthnStatement; +use LightSaml\Model\Assertion\Conditions; +use LightSaml\Model\Assertion\OneTimeUse; +use LightSaml\Model\Assertion\ProxyRestriction; +use LightSaml\SamlConstants; +use LightSaml\Validator\Model\NameId\NameIdValidatorInterface; +use LightSaml\Validator\Model\Statement\StatementValidatorInterface; +use LightSaml\Validator\Model\Subject\SubjectValidatorInterface; + +class AssertionValidator implements AssertionValidatorInterface +{ + /** @var NameIdValidatorInterface */ + protected $nameIdValidator; + + /** @var SubjectValidatorInterface */ + protected $subjectValidator; + + /** @var StatementValidatorInterface */ + protected $statementValidator; + + /** + * @param NameIdValidatorInterface $nameIdValidator + * @param SubjectValidatorInterface $subjectValidator + * @param StatementValidatorInterface $statementValidator + */ + public function __construct( + NameIdValidatorInterface $nameIdValidator, + SubjectValidatorInterface $subjectValidator, + StatementValidatorInterface $statementValidator + ) { + $this->nameIdValidator = $nameIdValidator; + $this->subjectValidator = $subjectValidator; + $this->statementValidator = $statementValidator; + } + + /** + * @param Assertion $assertion + * + * @return void + */ + public function validateAssertion(Assertion $assertion) + { + $this->validateAssertionAttributes($assertion); + $this->validateSubject($assertion); + $this->validateConditions($assertion); + $this->validateStatements($assertion); + } + + /** + * @param Assertion $assertion + * + * @throws LightSamlValidationException + */ + protected function validateAssertionAttributes(Assertion $assertion) + { + if (false == Helper::validateRequiredString($assertion->getVersion())) { + throw new LightSamlValidationException('Assertion element must have the Version attribute set.'); + } + if (SamlConstants::VERSION_20 != $assertion->getVersion()) { + throw new LightSamlValidationException('Assertion element must have the Version attribute value equal to 2.0.'); + } + if (false == Helper::validateRequiredString($assertion->getId())) { + throw new LightSamlValidationException('Assertion element must have the ID attribute set.'); + } + if (false == Helper::validateIdString($assertion->getId())) { + throw new LightSamlValidationException('Assertion element must have an ID attribute with at least 16 characters (the equivalent of 128 bits).'); + } + if (false == $assertion->getIssueInstantTimestamp()) { + throw new LightSamlValidationException('Assertion element must have the IssueInstant attribute set.'); + } + if (false == $assertion->getIssuer()) { + throw new LightSamlValidationException('Assertion element must have an issuer element.'); + } + $this->nameIdValidator->validateNameId($assertion->getIssuer()); + } + + /** + * @param Assertion $assertion + * + * @throws LightSamlValidationException + */ + protected function validateSubject(Assertion $assertion) + { + if (false == $assertion->getSubject()) { + if (false == $assertion->getAllItems()) { + throw new LightSamlValidationException('Assertion with no Statements must have a subject.'); + } + foreach ($assertion->getAllItems() as $item) { + if ($item instanceof AuthnStatement || $item instanceof AttributeStatement) { + throw new LightSamlValidationException('AuthnStatement, AuthzDecisionStatement and AttributeStatement require a subject.'); + } + } + } else { + $this->subjectValidator->validateSubject($assertion->getSubject()); + } + } + + protected function validateConditions(Assertion $assertion) + { + if (false == $assertion->getConditions()) { + return; + } + + $this->validateConditionsInterval($assertion->getConditions()); + + $oneTimeUseSeen = $proxyRestrictionSeen = false; + + foreach ($assertion->getConditions()->getAllItems() as $item) { + if ($item instanceof OneTimeUse) { + if ($oneTimeUseSeen) { + throw new LightSamlValidationException('Assertion contained more than one condition of type OneTimeUse'); + } + $oneTimeUseSeen = true; + } elseif ($item instanceof ProxyRestriction) { + if ($proxyRestrictionSeen) { + throw new LightSamlValidationException('Assertion contained more than one condition of type ProxyRestriction'); + } + $proxyRestrictionSeen = true; + + $this->validateProxyRestriction($item); + } elseif ($item instanceof AudienceRestriction) { + $this->validateAudienceRestriction($item); + } + } + } + + protected function validateConditionsInterval(Conditions $conditions) + { + if ($conditions->getNotBeforeTimestamp() && + $conditions->getNotOnOrAfterTimestamp() && + $conditions->getNotBeforeTimestamp() > $conditions->getNotOnOrAfterTimestamp() + ) { + throw new LightSamlValidationException('Conditions NotBefore MUST BE less than NotOnOrAfter'); + } + } + + /** + * @param ProxyRestriction $item + * + * @throws LightSamlValidationException + */ + protected function validateProxyRestriction(ProxyRestriction $item) + { + if (null === $item->getCount() || '' === $item->getCount() || intval($item->getCount()) != $item->getCount() || $item->getCount() < 0) { + throw new LightSamlValidationException('Count attribute of ProxyRestriction MUST BE a non-negative integer'); + } + + if ($item->getAllAudience()) { + foreach ($item->getAllAudience() as $audience) { + if (false == Helper::validateWellFormedUriString($audience)) { + throw new LightSamlValidationException('ProxyRestriction Audience MUST BE a wellformed uri'); + } + } + } + } + + /** + * @param AudienceRestriction $item + * + * @throws LightSamlValidationException + */ + protected function validateAudienceRestriction(AudienceRestriction $item) + { + if (false == $item->getAllAudience()) { + return; + } + + foreach ($item->getAllAudience() as $audience) { + if (false == Helper::validateWellFormedUriString($audience)) { + throw new LightSamlValidationException('AudienceRestriction MUST BE a wellformed uri'); + } + } + } + + /** + * @param Assertion $assertion + */ + protected function validateStatements(Assertion $assertion) + { + if (false == $assertion->getAllItems()) { + return; + } + + foreach ($assertion->getAllItems() as $statement) { + $this->statementValidator->validateStatement($statement); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Assertion/AssertionValidatorInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Assertion/AssertionValidatorInterface.php new file mode 100644 index 0000000000..865a9d2d5a --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Assertion/AssertionValidatorInterface.php @@ -0,0 +1,26 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Validator\Model\Assertion; + +use LightSaml\Model\Assertion\Assertion; + +interface AssertionValidatorInterface +{ + /** + * @param Assertion $assertion + * + * @throws \LightSaml\Error\LightSamlValidationException + * + * @return void + */ + public function validateAssertion(Assertion $assertion); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/NameId/NameIdValidator.php b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/NameId/NameIdValidator.php new file mode 100644 index 0000000000..73e6db90b4 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/NameId/NameIdValidator.php @@ -0,0 +1,179 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Validator\Model\NameId; + +use LightSaml\Error\LightSamlValidationException; +use LightSaml\Helper; +use LightSaml\Model\Assertion\AbstractNameID; +use LightSaml\SamlConstants; + +class NameIdValidator implements NameIdValidatorInterface +{ + private static $formatValidators = array( + SamlConstants::NAME_ID_FORMAT_EMAIL => 'validateEmailFormat', + SamlConstants::NAME_ID_FORMAT_X509_SUBJECT_NAME => 'validateX509SubjectNameFormat', + SamlConstants::NAME_ID_FORMAT_WINDOWS => 'validateWindowsFormat', + SamlConstants::NAME_ID_FORMAT_KERBEROS => 'validateKerberosFormat', + SamlConstants::NAME_ID_FORMAT_ENTITY => 'validateEntityFormat', + SamlConstants::NAME_ID_FORMAT_PERSISTENT => 'validatePersistentFormat', + SamlConstants::NAME_ID_FORMAT_TRANSIENT => 'validateTransientFormat', + ); + + /** + * @param AbstractNameID $nameId + * + * @throws \LightSaml\Error\LightSamlValidationException + * + * @return void + */ + public function validateNameId(AbstractNameID $nameId) + { + if (false == $nameId->getFormat()) { + return; + } + + $this->validateFormat($nameId); + + $validatorMethod = isset(self::$formatValidators[$nameId->getFormat()]) ? self::$formatValidators[$nameId->getFormat()] : null; + + if ($validatorMethod) { + $this->{$validatorMethod}($nameId); + } + } + + /** + * @param AbstractNameID $nameId + */ + protected function validateFormat(AbstractNameID $nameId) + { + if (false == Helper::validateWellFormedUriString($nameId->getFormat())) { + throw new LightSamlValidationException( + sprintf( + "NameID element has Format attribute '%s' which is not a wellformed absolute uri", + $nameId->getFormat() + ) + ); + } + } + + /** + * @param AbstractNameID $nameId + */ + protected function validateEmailFormat(AbstractNameID $nameId) + { + if (false == Helper::validateRequiredString($nameId->getValue())) { + throw new LightSamlValidationException('NameID with Email Format attribute MUST contain a Value that contains more than whitespace characters'); + } + + if (false == filter_var($nameId->getValue(), FILTER_VALIDATE_EMAIL)) { + throw new LightSamlValidationException('Value of NameID is not a valid email address according to the IETF RFC 2822 specification'); + } + } + + /** + * @param AbstractNameID $nameId + */ + protected function validateX509SubjectNameFormat(AbstractNameID $nameId) + { + if (false == Helper::validateRequiredString($nameId->getValue())) { + throw new LightSamlValidationException('NameID with X509SubjectName Format attribute MUST contain a Value that contains more than whitespace characters'); + } + + // TODO: Consider checking for correct encoding of the Value according to the + // XML Signature Recommendation (http://www.w3.org/TR/xmldsig-core/) section 4.4.4 + } + + /** + * @param AbstractNameID $nameId + */ + protected function validateWindowsFormat(AbstractNameID $nameId) + { + // Required format is 'DomainName\UserName' but the domain name and the '\' are optional + if (false == Helper::validateRequiredString($nameId->getValue())) { + throw new LightSamlValidationException('NameID with Windows Format attribute MUST contain a Value that contains more than whitespace characters'); + } + } + + /** + * @param AbstractNameID $nameId + */ + protected function validateKerberosFormat(AbstractNameID $nameId) + { + // Required format is 'name[/instance]@REALM' + if (false == Helper::validateRequiredString($nameId->getValue())) { + throw new LightSamlValidationException('NameID with Kerberos Format attribute MUST contain a Value that contains more than whitespace characters'); + } + if (strlen($nameId->getValue()) < 3) { + throw new LightSamlValidationException('NameID with Kerberos Format attribute MUST contain a Value with at least 3 characters'); + } + if (false === strpos($nameId->getValue(), '@')) { + throw new LightSamlValidationException("NameID with Kerberos Format attribute MUST contain a Value that contains a '@'"); + } + // TODO: Consider implementing the rules for 'name', 'instance' and 'REALM' found in IETF RFC 1510 (http://www.ietf.org/rfc/rfc1510.txt) here + } + + /** + * @param AbstractNameID $nameId + */ + protected function validateEntityFormat(AbstractNameID $nameId) + { + if (false == Helper::validateRequiredString($nameId->getValue())) { + throw new LightSamlValidationException('NameID with Entity Format attribute MUST contain a Value that contains more than whitespace characters'); + } + if (strlen($nameId->getValue()) > 1024) { + throw new LightSamlValidationException('NameID with Entity Format attribute MUST have a Value that contains no more than 1024 characters'); + } + if (false != $nameId->getNameQualifier()) { + throw new LightSamlValidationException('NameID with Entity Format attribute MUST NOT set the NameQualifier attribute'); + } + if (false != $nameId->getSPNameQualifier()) { + throw new LightSamlValidationException('NameID with Entity Format attribute MUST NOT set the SPNameQualifier attribute'); + } + if (false != $nameId->getSPProvidedID()) { + throw new LightSamlValidationException('NameID with Entity Format attribute MUST NOT set the SPProvidedID attribute'); + } + } + + /** + * @param AbstractNameID $nameId + */ + protected function validatePersistentFormat(AbstractNameID $nameId) + { + if (false == Helper::validateRequiredString($nameId->getValue())) { + throw new LightSamlValidationException('NameID with Persistent Format attribute MUST contain a Value that contains more than whitespace characters'); + } + if (strlen($nameId->getValue()) > 256) { + throw new LightSamlValidationException('NameID with Persistent Format attribute MUST have a Value that contains no more than 256 characters'); + } + } + + /** + * @param AbstractNameID $nameId + */ + protected function validateTransientFormat(AbstractNameID $nameId) + { + if (false == Helper::validateRequiredString($nameId->getValue())) { + throw new LightSamlValidationException('NameID with Transient Format attribute MUST contain a Value that contains more than whitespace characters'); + } + if (strlen($nameId->getValue()) > 256) { + throw new LightSamlValidationException('NameID with Transient Format attribute MUST have a Value that contains no more than 256 characters'); + } + if (false == Helper::validateIdString($nameId->getValue())) { + throw new LightSamlValidationException( + sprintf( + "NameID '%s' with Transient Format attribute MUST have a Value with at least 16 characters (the equivalent of 128 bits)", + $nameId->getValue() + ) + ); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/NameId/NameIdValidatorInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/NameId/NameIdValidatorInterface.php new file mode 100644 index 0000000000..2c4336842c --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/NameId/NameIdValidatorInterface.php @@ -0,0 +1,24 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Validator\Model\NameId; + +use LightSaml\Model\Assertion\AbstractNameID; + +interface NameIdValidatorInterface +{ + /** + * @param AbstractNameID $nameId + * + * @return void + */ + public function validateNameId(AbstractNameID $nameId); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Signature/SignatureValidator.php b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Signature/SignatureValidator.php new file mode 100644 index 0000000000..d458526485 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Signature/SignatureValidator.php @@ -0,0 +1,66 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Validator\Model\Signature; + +use LightSaml\Credential\CredentialInterface; +use LightSaml\Credential\Criteria\PublicKeyThumbprintCriteria; +use LightSaml\Error\LightSamlSecurityException; +use LightSaml\Model\XmlDSig\AbstractSignatureReader; +use LightSaml\Resolver\Credential\CredentialResolverInterface; +use LightSaml\SamlConstants; +use LightSaml\Credential\UsageType; +use LightSaml\Credential\Criteria\EntityIdCriteria; +use LightSaml\Credential\Criteria\MetadataCriteria; +use LightSaml\Credential\Criteria\UsageCriteria; + +class SignatureValidator implements SignatureValidatorInterface +{ + /** @var CredentialResolverInterface */ + protected $credentialResolver; + + /** + * @param CredentialResolverInterface $credentialResolver + */ + public function __construct(CredentialResolverInterface $credentialResolver) + { + $this->credentialResolver = $credentialResolver; + } + + /** + * @param AbstractSignatureReader $signature + * @param string $issuer + * @param string $metadataType + * + * @return CredentialInterface|null + */ + public function validate(AbstractSignatureReader $signature, $issuer, $metadataType) + { + $query = $this->credentialResolver->query(); + $query + ->add(new EntityIdCriteria($issuer)) + ->add(new MetadataCriteria($metadataType, SamlConstants::VERSION_20)) + ->add(new UsageCriteria(UsageType::SIGNING)) + ; + if ($signature->getKey() && $signature->getKey()->getX509Thumbprint()) { + $query->add(new PublicKeyThumbprintCriteria($signature->getKey()->getX509Thumbprint())); + } + $query->resolve(); + + $credentialCandidates = $query->allCredentials(); + if (empty($credentialCandidates)) { + throw new LightSamlSecurityException('No credentials resolved for signature verification'); + } + $credential = $signature->validateMulti($credentialCandidates); + + return $credential; + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Signature/SignatureValidatorInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Signature/SignatureValidatorInterface.php new file mode 100644 index 0000000000..3cc03602e6 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Signature/SignatureValidatorInterface.php @@ -0,0 +1,27 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Validator\Model\Signature; + +use LightSaml\Credential\CredentialInterface; +use LightSaml\Model\XmlDSig\AbstractSignatureReader; + +interface SignatureValidatorInterface +{ + /** + * @param AbstractSignatureReader $signature + * @param string $issuer + * @param string $metadataType + * + * @return CredentialInterface|null + */ + public function validate(AbstractSignatureReader $signature, $issuer, $metadataType); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Statement/StatementValidator.php b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Statement/StatementValidator.php new file mode 100644 index 0000000000..f2a102cb4d --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Statement/StatementValidator.php @@ -0,0 +1,116 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Validator\Model\Statement; + +use LightSaml\Error\LightSamlValidationException; +use LightSaml\Helper; +use LightSaml\Model\Assertion\AbstractStatement; +use LightSaml\Model\Assertion\Attribute; +use LightSaml\Model\Assertion\AttributeStatement; +use LightSaml\Model\Assertion\AuthnContext; +use LightSaml\Model\Assertion\AuthnStatement; + +class StatementValidator implements StatementValidatorInterface +{ + /** + * @param AbstractStatement $statement + * + * @throws \LightSaml\Error\LightSamlValidationException + * + * @return void + */ + public function validateStatement(AbstractStatement $statement) + { + if ($statement instanceof AuthnStatement) { + $this->validateAuthnStatement($statement); + } elseif ($statement instanceof AttributeStatement) { + $this->validateAttributeStatement($statement); + } else { + throw new LightSamlValidationException(sprintf("Unsupported Statement type '%s'", get_class($statement))); + } + } + + private function validateAuthnStatement(AuthnStatement $statement) + { + if (false == $statement->getAuthnInstantTimestamp()) { + throw new LightSamlValidationException('AuthnStatement MUST have an AuthnInstant attribute'); + } + if (false == Helper::validateOptionalString($statement->getSessionIndex())) { + throw new LightSamlValidationException('SessionIndex attribute of AuthnStatement must contain at least one non-whitespace character'); + } + if ($statement->getSubjectLocality()) { + if (false == Helper::validateOptionalString($statement->getSubjectLocality()->getAddress())) { + throw new LightSamlValidationException('Address attribute of SubjectLocality must contain at least one non-whitespace character'); + } + if (false == Helper::validateOptionalString($statement->getSubjectLocality()->getDnsName())) { + throw new LightSamlValidationException('DNSName attribute of SubjectLocality must contain at least one non-whitespace character'); + } + } + if (false == $statement->getAuthnContext()) { + throw new LightSamlValidationException('AuthnStatement MUST have an AuthnContext element'); + } + $this->validateAuthnContext($statement->getAuthnContext()); + } + + private function validateAuthnContext(AuthnContext $authnContext) + { + if (false == $authnContext->getAuthnContextClassRef() && + false == $authnContext->getAuthnContextDecl() && + false == $authnContext->getAuthnContextDeclRef() + ) { + throw new LightSamlValidationException('AuthnContext element MUST contain at least one AuthnContextClassRef, AuthnContextDecl or AuthnContextDeclRef element'); + } + + if ($authnContext->getAuthnContextClassRef() && + $authnContext->getAuthnContextDecl() && + $authnContext->getAuthnContextDeclRef() + ) { + throw new LightSamlValidationException('AuthnContext MUST NOT contain more than two elements.'); + } + + if ($authnContext->getAuthnContextClassRef()) { + if (false == Helper::validateWellFormedUriString($authnContext->getAuthnContextClassRef())) { + throw new LightSamlValidationException('AuthnContextClassRef has a value which is not a wellformed absolute uri'); + } + } + if ($authnContext->getAuthnContextDeclRef()) { + if (false === Helper::validateWellFormedUriString($authnContext->getAuthnContextDeclRef())) { + throw new LightSamlValidationException('AuthnContextDeclRef has a value which is not a wellformed absolute uri'); + } + } + } + + private function validateAttributeStatement(AttributeStatement $statement) + { + if (false == $statement->getAllAttributes()) { + throw new LightSamlValidationException('AttributeStatement MUST contain at least one Attribute or EncryptedAttribute'); + } + + foreach ($statement->getAllAttributes() as $attribute) { + $this->validateAttribute($attribute); + } + } + + /** + * @param Attribute $attribute + * + * @throws LightSamlValidationException + * + * @return void + */ + private function validateAttribute(Attribute $attribute) + { + if (false == Helper::validateRequiredString($attribute->getName())) { + throw new LightSamlValidationException('Name attribute of Attribute element MUST contain at least one non-whitespace character'); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Statement/StatementValidatorInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Statement/StatementValidatorInterface.php new file mode 100644 index 0000000000..a0f280c8b3 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Statement/StatementValidatorInterface.php @@ -0,0 +1,24 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Validator\Model\Statement; + +use LightSaml\Model\Assertion\AbstractStatement; + +interface StatementValidatorInterface +{ + /** + * @param AbstractStatement $statement + * + * @return void + */ + public function validateStatement(AbstractStatement $statement); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Subject/SubjectValidator.php b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Subject/SubjectValidator.php new file mode 100644 index 0000000000..2c55f814c2 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Subject/SubjectValidator.php @@ -0,0 +1,93 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Validator\Model\Subject; + +use LightSaml\Error\LightSamlValidationException; +use LightSaml\Helper; +use LightSaml\Model\Assertion\Subject; +use LightSaml\Model\Assertion\SubjectConfirmation; +use LightSaml\Model\Assertion\SubjectConfirmationData; +use LightSaml\Validator\Model\NameId\NameIdValidatorInterface; + +class SubjectValidator implements SubjectValidatorInterface +{ + /** @var NameIdValidatorInterface */ + protected $nameIdValidator; + + /** + * @param NameIdValidatorInterface $nameIdValidator + */ + public function __construct(NameIdValidatorInterface $nameIdValidator) + { + $this->nameIdValidator = $nameIdValidator; + } + + /** + * @param Subject $subject + * + * @throws LightSamlValidationException + * + * @return void + */ + public function validateSubject(Subject $subject) + { + if (false == $subject->getNameID() && + false == $subject->getAllSubjectConfirmations() + ) { + throw new LightSamlValidationException('Subject MUST contain either an identifier or a subject confirmation'); + } + + if ($subject->getNameID()) { + $this->nameIdValidator->validateNameId($subject->getNameID()); + } + + foreach ($subject->getAllSubjectConfirmations() as $subjectConfirmation) { + $this->validateSubjectConfirmation($subjectConfirmation); + } + } + + /** + * @param SubjectConfirmation $subjectConfirmation + * + * @throws \LightSaml\Error\LightSamlValidationException + */ + protected function validateSubjectConfirmation(SubjectConfirmation $subjectConfirmation) + { + if (false == Helper::validateRequiredString($subjectConfirmation->getMethod())) { + throw new LightSamlValidationException('Method attribute of SubjectConfirmation MUST contain at least one non-whitespace character'); + } + if (false == Helper::validateWellFormedUriString($subjectConfirmation->getMethod())) { + throw new LightSamlValidationException('SubjectConfirmation element has Method attribute which is not a wellformed absolute uri.'); + } + if ($subjectConfirmation->getNameID()) { + $this->nameIdValidator->validateNameId($subjectConfirmation->getNameID()); + } + if ($subjectConfirmation->getSubjectConfirmationData()) { + $this->validateSubjectConfirmationData($subjectConfirmation->getSubjectConfirmationData()); + } + } + + protected function validateSubjectConfirmationData(SubjectConfirmationData $subjectConfirmationData) + { + if ($subjectConfirmationData->getRecipient()) { + if (false == Helper::validateWellFormedUriString($subjectConfirmationData->getRecipient())) { + throw new LightSamlValidationException('Recipient of SubjectConfirmationData must be a wellformed absolute URI.'); + } + } + if ($subjectConfirmationData->getNotBeforeTimestamp() && + $subjectConfirmationData->getNotOnOrAfterTimestamp() && + $subjectConfirmationData->getNotBeforeTimestamp() >= $subjectConfirmationData->getNotOnOrAfterTimestamp() + ) { + throw new LightSamlValidationException('SubjectConfirmationData NotBefore MUST be less than NotOnOrAfter'); + } + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Subject/SubjectValidatorInterface.php b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Subject/SubjectValidatorInterface.php new file mode 100644 index 0000000000..e7a89ba71a --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Subject/SubjectValidatorInterface.php @@ -0,0 +1,24 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Validator\Model\Subject; + +use LightSaml\Model\Assertion\Subject; + +interface SubjectValidatorInterface +{ + /** + * @param Subject $subject + * + * @return void + */ + public function validateSubject(Subject $subject); +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Xsd/XsdError.php b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Xsd/XsdError.php new file mode 100644 index 0000000000..3c57b289f3 --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Xsd/XsdError.php @@ -0,0 +1,124 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Validator\Model\Xsd; + +class XsdError +{ + const WARNING = 'Warning'; + const ERROR = 'Error'; + const FATAL = 'Fatal'; + + private static $levelMap = [ + LIBXML_ERR_WARNING => self::WARNING, + LIBXML_ERR_ERROR => self::ERROR, + LIBXML_ERR_FATAL => self::FATAL, + ]; + + /** @var string */ + private $level; + + /** @var string */ + private $code; + + /** @var string */ + private $message; + + /** @var string */ + private $line; + + /** @var string */ + private $column; + + /** + * @param \LibXMLError $error + * + * @return XsdError + */ + public static function fromLibXMLError(\LibXMLError $error) + { + return new self( + isset(self::$levelMap[$error->level]) ? self::$levelMap[$error->level] : 'Unknown', + $error->code, + $error->message, + $error->line, + $error->column + ); + } + + /** + * @param string $level + * @param string $code + * @param string $message + * @param string $line + * @param string $column + */ + public function __construct($level, $code, $message, $line, $column) + { + $this->level = $level; + $this->code = $code; + $this->message = $message; + $this->line = $line; + $this->column = $column; + } + + /** + * @return string + */ + public function getLevel() + { + return $this->level; + } + + /** + * @return string + */ + public function getCode() + { + return $this->code; + } + + /** + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * @return string + */ + public function getLine() + { + return $this->line; + } + + /** + * @return string + */ + public function getColumn() + { + return $this->column; + } + + public function __toString() + { + return sprintf( + '%s %s: %s on line %s column %s', + $this->level, + $this->code, + trim($this->message), + $this->line, + $this->column + ); + } +} diff --git a/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Xsd/XsdValidator.php b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Xsd/XsdValidator.php new file mode 100644 index 0000000000..4e8e22d36a --- /dev/null +++ b/vendor/lightsaml/lightsaml/src/LightSaml/Validator/Model/Xsd/XsdValidator.php @@ -0,0 +1,82 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace LightSaml\Validator\Model\Xsd; + +use LightSaml\Error\LightSamlXmlException; + +class XsdValidator +{ + /** + * @param string $xml + * + * @return XsdError[] + */ + public function validateProtocol($xml) + { + return $this->validate($xml, 'saml-schema-protocol-2.0.xsd'); + } + + /** + * @param string $xml + * + * @return XsdError[] + */ + public function validateMetadata($xml) + { + return $this->validate($xml, 'saml-schema-metadata-2.0.xsd'); + } + + /** + * @param string $xml + * @param string $schema + * + * @return XsdError[] + */ + private function validate($xml, $schema) + { + $result = []; + libxml_clear_errors(); + $doc = new \DOMDocument(); + + set_error_handler(function ($errno, $errstr, $errfile, $errline, array $errcontext) use (&$result) { + $error = new XsdError(XsdError::FATAL, $errno, $errstr, 0, 0); + $result[] = $error; + }); + + $schemaFile = __DIR__.'/../../../../../xsd/'.$schema; + if (!is_file($schemaFile)) { + throw new LightSamlXmlException('Invalid schema specified'); + } + + $ok = @$doc->loadXML($xml); + if (!$ok) { + restore_error_handler(); + + return [ + new XsdError(XsdError::FATAL, 0, 'Invalid XML', 0, 0), + ]; + } + + @$doc->schemaValidate($schemaFile); + + /** @var \LibXMLError[] $errors */ + $errors = libxml_get_errors(); + foreach ($errors as $error) { + $err = XsdError::fromLibXMLError($error); + $result[] = $err; + } + + restore_error_handler(); + + return $result; + } +} diff --git a/vendor/lightsaml/lightsaml/web/sp/_config.php b/vendor/lightsaml/lightsaml/web/sp/_config.php new file mode 100644 index 0000000000..a075cc4cc6 --- /dev/null +++ b/vendor/lightsaml/lightsaml/web/sp/_config.php @@ -0,0 +1,199 @@ +buildOwnContext($result); + $this->buildSystemContext($result); + $this->buildPartyContext($result); + $this->buildStoreContext($result); + $this->buildProviderContext($result); + $this->buildCredentialContext($result); + $this->buildServiceContext($result); + + return $result; + } + + private function buildOwnContext(\LightSaml\Bridge\Pimple\Container\BuildContainer $buildContainer) + { + $ownCredential = $this->buildOwnCredential(); + $ownEntityDescriptorProvider = $this->buildOwnEntityDescriptorProvider($ownCredential->getCertificate()); + + $buildContainer->getPimple()->register( + new \LightSaml\Bridge\Pimple\Container\Factory\OwnContainerProvider( + $ownEntityDescriptorProvider, + [$ownCredential] + ) + ); + } + + private function buildSystemContext(\LightSaml\Bridge\Pimple\Container\BuildContainer $buildContainer) + { + $buildContainer->getPimple()->register(new \LightSaml\Bridge\Pimple\Container\Factory\SystemContainerProvider()); + + $pimple = $buildContainer->getPimple(); + $pimple[\LightSaml\Bridge\Pimple\Container\SystemContainer::LOGGER] = function () { + return $this->buildLogger(); + + }; + $pimple[\LightSaml\Bridge\Pimple\Container\SystemContainer::SESSION] = function () { + return $this->buildSession(); + + }; + } + + private function buildPartyContext(\LightSaml\Bridge\Pimple\Container\BuildContainer $buildContainer) + { + $buildContainer->getPimple()->register(new \LightSaml\Bridge\Pimple\Container\Factory\PartyContainerProvider()); + + $pimple = $buildContainer->getPimple(); + $pimple[\LightSaml\Bridge\Pimple\Container\PartyContainer::IDP_ENTITY_DESCRIPTOR] = function () { + return $this->buildIdpEntityStore(); + }; + $pimple[\LightSaml\Bridge\Pimple\Container\PartyContainer::TRUST_OPTIONS_STORE] = function () { + $trustOptions = new \LightSaml\Meta\TrustOptions\TrustOptions(); + $trustOptions->setSignAuthnRequest(true); + + return new \LightSaml\Store\TrustOptions\FixedTrustOptionsStore($trustOptions); + }; + } + + private function buildStoreContext(\LightSaml\Bridge\Pimple\Container\BuildContainer $buildContainer) + { + $buildContainer->getPimple()->register( + new \LightSaml\Bridge\Pimple\Container\Factory\StoreContainerProvider( + $buildContainer->getSystemContainer() + ) + ); + } + + private function buildProviderContext(\LightSaml\Bridge\Pimple\Container\BuildContainer $buildContainer) + { + $buildContainer->getPimple()->register( + new \LightSaml\Bridge\Pimple\Container\Factory\ProviderContainerProvider() + ); + } + + private function buildCredentialContext(\LightSaml\Bridge\Pimple\Container\BuildContainer $buildContainer) + { + $buildContainer->getPimple()->register( + new \LightSaml\Bridge\Pimple\Container\Factory\CredentialContainerProvider( + $buildContainer->getPartyContainer(), + $buildContainer->getOwnContainer() + ) + ); + } + + private function buildServiceContext(\LightSaml\Bridge\Pimple\Container\BuildContainer $buildContainer) + { + $buildContainer->getPimple()->register( + new \LightSaml\Bridge\Pimple\Container\Factory\ServiceContainerProvider( + $buildContainer->getCredentialContainer(), + $buildContainer->getStoreContainer(), + $buildContainer->getSystemContainer() + ) + ); + } + + /** + * @return \Symfony\Component\HttpFoundation\Session\Session + */ + private function buildSession() + { + $session = new \Symfony\Component\HttpFoundation\Session\Session(); + $session->setName('PHPSIDSP'); + $session->start(); + + return $session; + } + + /** + * @return \LightSaml\Credential\X509Credential + */ + private function buildOwnCredential() + { + $ownCredential = new \LightSaml\Credential\X509Credential( + (new \LightSaml\Credential\X509Certificate()) + ->loadPem(file_get_contents(__DIR__.'/saml.crt')), + \LightSaml\Credential\KeyHelper::createPrivateKey(__DIR__.'/saml.key', null, true) + ); + $ownCredential + ->setEntityId(self::OWN_ENTITY_ID) + ; + + return $ownCredential; + } + + /** + * @param \LightSaml\Credential\X509Certificate $certificate + * + * @return \LightSaml\Provider\EntityDescriptor\EntityDescriptorProviderInterface + */ + private function buildOwnEntityDescriptorProvider(\LightSaml\Credential\X509Certificate $certificate) + { + return new \LightSaml\Builder\EntityDescriptor\SimpleEntityDescriptorBuilder( + self::OWN_ENTITY_ID, + 'https://localhost/lightsaml/lightSAML/web/sp/acs.php', + null, + $certificate + ); + } + + /** + * @return \LightSaml\Store\EntityDescriptor\FixedEntityDescriptorStore + */ + private function buildIdpEntityStore() + { + $idpProvider = new \LightSaml\Store\EntityDescriptor\FixedEntityDescriptorStore(); + $idpProvider->add( + \LightSaml\Model\Metadata\EntitiesDescriptor::load(__DIR__.'/testshib-providers.xml') + ); + $idpProvider->add( + \LightSaml\Model\Metadata\EntityDescriptor::load(__DIR__.'/localhost-lightsaml-lightsaml-idp.xml') + ); + $idpProvider->add( + \LightSaml\Model\Metadata\EntityDescriptor::load(__DIR__.'/openidp.feide.no.xml') + ); + $idpProvider->add( + \LightSaml\Model\Metadata\EntityDescriptor::load(__DIR__.'/FederationMetadata.xml') + ); + + return $idpProvider; + } + + /** + * @return \Monolog\Logger + */ + private function buildLogger() + { + $logger = new \Monolog\Logger('lightsaml', array(new \Monolog\Handler\StreamHandler(__DIR__.'/sp.log'))); + + return $logger; + } +} diff --git a/vendor/lightsaml/lightsaml/web/sp/acs.php b/vendor/lightsaml/lightsaml/web/sp/acs.php new file mode 100644 index 0000000000..d69915bb25 --- /dev/null +++ b/vendor/lightsaml/lightsaml/web/sp/acs.php @@ -0,0 +1,53 @@ +getBuildContainer(); +$builder = new \LightSaml\Builder\Profile\WebBrowserSso\Sp\SsoSpReceiveResponseProfileBuilder($buildContainer); + +$context = $builder->buildContext(); +$action = $builder->buildAction(); + +if (SpConfig::current()->debug) { + var_dump('ACTION TREE'); + var_dump($action->__toString()); +} + +try { + $action->execute($context); +} catch (\Exception $ex) { + var_dump('CONTEXT TREE'); + var_dump($context->__toString()); + throw new \RuntimeException('Error', 0, $ex); +} + +var_dump('CONTEXT TREE'); +var_dump($context->__toString()); + +$response = \LightSaml\Context\Profile\Helper\MessageContextHelper::asResponse($context->getInboundContext()); + +var_dump('RELAY STATE'); +var_dump($response->getRelayState()); + +var_dump('ATTRIBUTES'); +foreach ($response->getAllAssertions() as $assertion) { + foreach ($assertion->getAllAttributeStatements() as $attributeStatement) { + foreach ($attributeStatement->getAllAttributes() as $attribute) { + var_dump($attribute); + } + } +} + +/** @var \LightSaml\Model\Context\DeserializationContext $inboundMessageDeserializationContext */ +$inboundMessageDeserializationContext = $context->getPath('inbound_message/deserialization'); +$inboundMessageDeserializationContext->getDocument()->formatOutput = true; +var_dump('RECEIVED MESSAGE'); +var_dump($inboundMessageDeserializationContext->getDocument()->saveXML()); + +/** @var \LightSaml\Model\Context\DeserializationContext $decryptedAssertionContext */ +$decryptedAssertionContext = $context->getPath('inbound_message/assertion_encrypted_0'); +if ($decryptedAssertionContext) { + $decryptedAssertionContext->getDocument()->formatOutput = true; + var_dump('DECRYPTED ASSERTION'); + var_dump($decryptedAssertionContext->getDocument()->saveXML()); +} diff --git a/vendor/lightsaml/lightsaml/web/sp/discovery.php b/vendor/lightsaml/lightsaml/web/sp/discovery.php new file mode 100644 index 0000000000..ad8d4b87e7 --- /dev/null +++ b/vendor/lightsaml/lightsaml/web/sp/discovery.php @@ -0,0 +1,22 @@ +getBuildContainer()->getPartyContainer()->getIdpEntityDescriptorStore()->all(); +switch (count($all)) { + case 0: + print "None IDP configured"; + exit; + case 1: + header('Location: login.php?idp='.$all[0]->getEntityID()); + exit; +} + +print "

Following IDPs are configured

\n"; +print "

Choose one

\n"; +foreach ($all as $idp) { + if ($idp->getAllIdpSsoDescriptors()) { + print "

getEntityID()}\">{$idp->getEntityID()}

\n"; + } +} +print "\n

LigthSAML

\n"; diff --git a/vendor/lightsaml/lightsaml/web/sp/localhost-lightsaml-lightsaml-idp.xml b/vendor/lightsaml/lightsaml/web/sp/localhost-lightsaml-lightsaml-idp.xml new file mode 100644 index 0000000000..d54456dff8 --- /dev/null +++ b/vendor/lightsaml/lightsaml/web/sp/localhost-lightsaml-lightsaml-idp.xml @@ -0,0 +1,21 @@ + + + + + + + MIIDyjCCArKgAwIBAgIJAJNOFuQd727cMA0GCSqGSIb3DQEBBQUAMEwxCzAJBgNVBAYTAlJTMREwDwYDVQQIEwhCZWxncmFkZTESMBAGA1UEChMJTGlnaHRTQU1MMRYwFAYDVQQDEw1saWdodHNhbWwuY29tMB4XDTE1MDkxMzE5MDE0MFoXDTI1MDkxMDE5MDE0MFowTDELMAkGA1UEBhMCUlMxETAPBgNVBAgTCEJlbGdyYWRlMRIwEAYDVQQKEwlMaWdodFNBTUwxFjAUBgNVBAMTDWxpZ2h0c2FtbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7pUKOPMyE2oScHLPGJFTepK9j1H03e/s/WnONw8ZwYBaBIYIQuX6uE8jFPdD0uQSaYpOw5h5Tgq6xBV7m2kPO53hs8gEGWRbCdCtxi9EMJwIOYr+isG0N+DvV9KybJf6tqcM50PiFjVNtfx8IubMpAKCbquaqdLaHH0rgP1hbgnGm5YZkyEK4s8xuLUDS6qL7N7a/ez2Zk45u3L3qFcuncPI5BTnJg6fqlypDhCDOBI5Ljw10HmgZHPIXzOhEPVV+rX2iHhF4V9vzEoeIUABYXQVNRRNHpPdVsK6iTTkyvbrGJ/tv3oFZhNOSL0Kuy+Q9nlE9fEFqyUydJ67vsXqZAgMBAAGjga4wgaswHQYDVR0OBBYEFHPT6Ey1qgxMzMIt2d3OWuwzfPSUMHwGA1UdIwR1MHOAFHPT6Ey1qgxMzMIt2d3OWuwzfPSUoVCkTjBMMQswCQYDVQQGEwJSUzERMA8GA1UECBMIQmVsZ3JhZGUxEjAQBgNVBAoTCUxpZ2h0U0FNTDEWMBQGA1UEAxMNbGlnaHRzYW1sLmNvbYIJAJNOFuQd727cMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHkHtwJBoeOhvr06M0MikKc99ze6TqAGvf+QkgFoV1sWGAh3NKcAR+XSlfK+sQWrHGkiia5hWKgAPMMUbkLP9DFWkjbK241isCZZD/LvA1anbV+7Pidn+swZ5dR7ynX2vj0kFYb+VsGPkavNcj8RN/DduhN/Tmi5sQAlWhaw06UAeEqXtFeLbTgLffBaj7PmR0IYjvTZA0X2FdRu0GXRxn7zghjpvSq9nuWa3pGbfdVtL6GIkwYUPcDzjr4OeGXNmIZe/wMCnz6VGZY+LUgzi/4DAC6V3OjMuhdqS/2+o1+CXCwN08CIHQV6+AUBenEVawMsiadLBgx3kFe5iXrYRMA= + + + + + + + MIIDyjCCArKgAwIBAgIJAJNOFuQd727cMA0GCSqGSIb3DQEBBQUAMEwxCzAJBgNVBAYTAlJTMREwDwYDVQQIEwhCZWxncmFkZTESMBAGA1UEChMJTGlnaHRTQU1MMRYwFAYDVQQDEw1saWdodHNhbWwuY29tMB4XDTE1MDkxMzE5MDE0MFoXDTI1MDkxMDE5MDE0MFowTDELMAkGA1UEBhMCUlMxETAPBgNVBAgTCEJlbGdyYWRlMRIwEAYDVQQKEwlMaWdodFNBTUwxFjAUBgNVBAMTDWxpZ2h0c2FtbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7pUKOPMyE2oScHLPGJFTepK9j1H03e/s/WnONw8ZwYBaBIYIQuX6uE8jFPdD0uQSaYpOw5h5Tgq6xBV7m2kPO53hs8gEGWRbCdCtxi9EMJwIOYr+isG0N+DvV9KybJf6tqcM50PiFjVNtfx8IubMpAKCbquaqdLaHH0rgP1hbgnGm5YZkyEK4s8xuLUDS6qL7N7a/ez2Zk45u3L3qFcuncPI5BTnJg6fqlypDhCDOBI5Ljw10HmgZHPIXzOhEPVV+rX2iHhF4V9vzEoeIUABYXQVNRRNHpPdVsK6iTTkyvbrGJ/tv3oFZhNOSL0Kuy+Q9nlE9fEFqyUydJ67vsXqZAgMBAAGjga4wgaswHQYDVR0OBBYEFHPT6Ey1qgxMzMIt2d3OWuwzfPSUMHwGA1UdIwR1MHOAFHPT6Ey1qgxMzMIt2d3OWuwzfPSUoVCkTjBMMQswCQYDVQQGEwJSUzERMA8GA1UECBMIQmVsZ3JhZGUxEjAQBgNVBAoTCUxpZ2h0U0FNTDEWMBQGA1UEAxMNbGlnaHRzYW1sLmNvbYIJAJNOFuQd727cMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHkHtwJBoeOhvr06M0MikKc99ze6TqAGvf+QkgFoV1sWGAh3NKcAR+XSlfK+sQWrHGkiia5hWKgAPMMUbkLP9DFWkjbK241isCZZD/LvA1anbV+7Pidn+swZ5dR7ynX2vj0kFYb+VsGPkavNcj8RN/DduhN/Tmi5sQAlWhaw06UAeEqXtFeLbTgLffBaj7PmR0IYjvTZA0X2FdRu0GXRxn7zghjpvSq9nuWa3pGbfdVtL6GIkwYUPcDzjr4OeGXNmIZe/wMCnz6VGZY+LUgzi/4DAC6V3OjMuhdqS/2+o1+CXCwN08CIHQV6+AUBenEVawMsiadLBgx3kFe5iXrYRMA= + + + + + + + diff --git a/vendor/lightsaml/lightsaml/web/sp/login.php b/vendor/lightsaml/lightsaml/web/sp/login.php new file mode 100644 index 0000000000..f7ab19bb12 --- /dev/null +++ b/vendor/lightsaml/lightsaml/web/sp/login.php @@ -0,0 +1,40 @@ +getBuildContainer(); +$builder = new \LightSaml\Builder\Profile\WebBrowserSso\Sp\SsoSpSendAuthnRequestProfileBuilder($buildContainer, $idpEntityId); + +$buildContainer->getSystemContainer()->getEventDispatcher() + ->addListener( + \LightSaml\Event\Events::BINDING_MESSAGE_SENT, + function (\Symfony\Component\EventDispatcher\GenericEvent $event) { + //var_dump($event->getSubject()); + //exit; + } + ); +$buildContainer->getSystemContainer()->getEventDispatcher() + ->addListener( + \LightSaml\Event\Events::BEFORE_ENCRYPT, + function (\Symfony\Component\EventDispatcher\GenericEvent $event) { + /** @var \LightSaml\Context\Profile\ProfileContext $context */ + $context = $event->getSubject(); + $context->getOutboundMessage()->setRelayState('relayState'); + } + ); + +$context = $builder->buildContext(); +$action = $builder->buildAction(); +$action->map(function (\LightSaml\Action\ActionInterface $action) use ($buildContainer) { + return new \LightSaml\Action\LoggableAction($action, $buildContainer->getSystemContainer()->getLogger()); +}); + +$action->execute($context); + +$context->getHttpResponseContext()->getResponse()->send(); diff --git a/vendor/lightsaml/lightsaml/web/sp/metadata.php b/vendor/lightsaml/lightsaml/web/sp/metadata.php new file mode 100644 index 0000000000..aa1828618f --- /dev/null +++ b/vendor/lightsaml/lightsaml/web/sp/metadata.php @@ -0,0 +1,19 @@ +getBuildContainer() +); + +$context = $builder->buildContext(); +$action = $builder->buildAction(); + +//print "
\n";
+//print_r($action->debugPrintTree());
+//
+//exit;
+
+$action->execute($context);
+
+$context->getHttpResponseContext()->getResponse()->send();
diff --git a/vendor/lightsaml/lightsaml/web/sp/openidp.feide.no.xml b/vendor/lightsaml/lightsaml/web/sp/openidp.feide.no.xml
new file mode 100644
index 0000000000..1173311fef
--- /dev/null
+++ b/vendor/lightsaml/lightsaml/web/sp/openidp.feide.no.xml
@@ -0,0 +1,27 @@
+
+
+  
+    
+      
+        
+          MIICizCCAfQCCQCY8tKaMc0BMjANBgkqhkiG9w0BAQUFADCBiTELMAkGA1UEBhMCTk8xEjAQBgNVBAgTCVRyb25kaGVpbTEQMA4GA1UEChMHVU5JTkVUVDEOMAwGA1UECxMFRmVpZGUxGTAXBgNVBAMTEG9wZW5pZHAuZmVpZGUubm8xKTAnBgkqhkiG9w0BCQEWGmFuZHJlYXMuc29sYmVyZ0B1bmluZXR0Lm5vMB4XDTA4MDUwODA5MjI0OFoXDTM1MDkyMzA5MjI0OFowgYkxCzAJBgNVBAYTAk5PMRIwEAYDVQQIEwlUcm9uZGhlaW0xEDAOBgNVBAoTB1VOSU5FVFQxDjAMBgNVBAsTBUZlaWRlMRkwFwYDVQQDExBvcGVuaWRwLmZlaWRlLm5vMSkwJwYJKoZIhvcNAQkBFhphbmRyZWFzLnNvbGJlcmdAdW5pbmV0dC5ubzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAt8jLoqI1VTlxAZ2axiDIThWcAOXdu8KkVUWaN/SooO9O0QQ7KRUjSGKN9JK65AFRDXQkWPAu4HlnO4noYlFSLnYyDxI66LCr71x4lgFJjqLeAvB/GqBqFfIZ3YK/NrhnUqFwZu63nLrZjcUZxNaPjOOSRSDaXpv1kb5k3jOiSGECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBQYj4cAafWaYfjBU2zi1ElwStIaJ5nyp/s/8B8SAPK2T79McMyccP3wSW13LHkmM1jwKe3ACFXBvqGQN0IbcH49hu0FKhYFM/GPDJcIHFBsiyMBXChpye9vBaTNEBCtU3KjjyG0hRT2mAQ9h+bkPmOvlEo/aH0xR68Z9hw4PF13w==
+        
+      
+    
+    
+      
+        
+          MIICizCCAfQCCQCY8tKaMc0BMjANBgkqhkiG9w0BAQUFADCBiTELMAkGA1UEBhMCTk8xEjAQBgNVBAgTCVRyb25kaGVpbTEQMA4GA1UEChMHVU5JTkVUVDEOMAwGA1UECxMFRmVpZGUxGTAXBgNVBAMTEG9wZW5pZHAuZmVpZGUubm8xKTAnBgkqhkiG9w0BCQEWGmFuZHJlYXMuc29sYmVyZ0B1bmluZXR0Lm5vMB4XDTA4MDUwODA5MjI0OFoXDTM1MDkyMzA5MjI0OFowgYkxCzAJBgNVBAYTAk5PMRIwEAYDVQQIEwlUcm9uZGhlaW0xEDAOBgNVBAoTB1VOSU5FVFQxDjAMBgNVBAsTBUZlaWRlMRkwFwYDVQQDExBvcGVuaWRwLmZlaWRlLm5vMSkwJwYJKoZIhvcNAQkBFhphbmRyZWFzLnNvbGJlcmdAdW5pbmV0dC5ubzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAt8jLoqI1VTlxAZ2axiDIThWcAOXdu8KkVUWaN/SooO9O0QQ7KRUjSGKN9JK65AFRDXQkWPAu4HlnO4noYlFSLnYyDxI66LCr71x4lgFJjqLeAvB/GqBqFfIZ3YK/NrhnUqFwZu63nLrZjcUZxNaPjOOSRSDaXpv1kb5k3jOiSGECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBQYj4cAafWaYfjBU2zi1ElwStIaJ5nyp/s/8B8SAPK2T79McMyccP3wSW13LHkmM1jwKe3ACFXBvqGQN0IbcH49hu0FKhYFM/GPDJcIHFBsiyMBXChpye9vBaTNEBCtU3KjjyG0hRT2mAQ9h+bkPmOvlEo/aH0xR68Z9hw4PF13w==
+        
+      
+    
+    
+    urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+    
+  
+  
+    Feide
+    support
+    support@feide.no
+  
+
\ No newline at end of file
diff --git a/vendor/lightsaml/lightsaml/web/sp/saml.crt b/vendor/lightsaml/lightsaml/web/sp/saml.crt
new file mode 100644
index 0000000000..cd0ca88518
--- /dev/null
+++ b/vendor/lightsaml/lightsaml/web/sp/saml.crt
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIIDyjCCArKgAwIBAgIJANZLMiMszO+tMA0GCSqGSIb3DQEBBQUAMEwxCzAJBgNV
+BAYTAlJTMREwDwYDVQQIEwhCZWxncmFkZTESMBAGA1UEChMJTGlnaHRTQU1MMRYw
+FAYDVQQDEw1saWdodHNhbWwuY29tMB4XDTE1MDkxMzE4MzU0NloXDTI1MDkxMDE4
+MzU0NlowTDELMAkGA1UEBhMCUlMxETAPBgNVBAgTCEJlbGdyYWRlMRIwEAYDVQQK
+EwlMaWdodFNBTUwxFjAUBgNVBAMTDWxpZ2h0c2FtbC5jb20wggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC7pUKOPMyE2oScHLPGJFTepK9j1H03e/s/WnON
+w8ZwYBaBIYIQuX6uE8jFPdD0uQSaYpOw5h5Tgq6xBV7m2kPO53hs8gEGWRbCdCtx
+i9EMJwIOYr+isG0N+DvV9KybJf6tqcM50PiFjVNtfx8IubMpAKCbquaqdLaHH0rg
+P1hbgnGm5YZkyEK4s8xuLUDS6qL7N7a/ez2Zk45u3L3qFcuncPI5BTnJg6fqlypD
+hCDOBI5Ljw10HmgZHPIXzOhEPVV+rX2iHhF4V9vzEoeIUABYXQVNRRNHpPdVsK6i
+TTkyvbrGJ/tv3oFZhNOSL0Kuy+Q9nlE9fEFqyUydJ67vsXqZAgMBAAGjga4wgasw
+HQYDVR0OBBYEFHPT6Ey1qgxMzMIt2d3OWuwzfPSUMHwGA1UdIwR1MHOAFHPT6Ey1
+qgxMzMIt2d3OWuwzfPSUoVCkTjBMMQswCQYDVQQGEwJSUzERMA8GA1UECBMIQmVs
+Z3JhZGUxEjAQBgNVBAoTCUxpZ2h0U0FNTDEWMBQGA1UEAxMNbGlnaHRzYW1sLmNv
+bYIJANZLMiMszO+tMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAE0H
+xNZpi/gSVrkhQ756AgIC25l6A4C6xZ8iAZiBApJcVdUZytBgpzypFSd8yg7Yh5P3
+ftlDjYEMB/uIvBsKe6HQyUy90VrSi4aaGC/7ilj6DTCX3jeuuH1JnU6sBxhN9IiJ
+RY3DbMzY5KAdtK/1fYlKa6PugXruJWrB3bC1VaFWLjMytnvaEQxjam4bsj1sF0+v
+6jL3RIQzdW9jJ7Udoul5fGR56A0Uhi0lqObPKI2lIK1psWXLwksdvO9NNt9Vm27Q
+LlklvpYuIh086wLmbiVmO+VQxDYwPmL8NEiLSA4Po/q7n+qV7Vx/EtIKr7lwZ2Mi
+cv5Xm0sequAbt3dnqPI=
+-----END CERTIFICATE-----
diff --git a/vendor/lightsaml/lightsaml/web/sp/saml.key b/vendor/lightsaml/lightsaml/web/sp/saml.key
new file mode 100644
index 0000000000..36df92cd16
--- /dev/null
+++ b/vendor/lightsaml/lightsaml/web/sp/saml.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAu6VCjjzMhNqEnByzxiRU3qSvY9R9N3v7P1pzjcPGcGAWgSGC
+ELl+rhPIxT3Q9LkEmmKTsOYeU4KusQVe5tpDzud4bPIBBlkWwnQrcYvRDCcCDmK/
+orBtDfg71fSsmyX+ranDOdD4hY1TbX8fCLmzKQCgm6rmqnS2hx9K4D9YW4JxpuWG
+ZMhCuLPMbi1A0uqi+ze2v3s9mZOObty96hXLp3DyOQU5yYOn6pcqQ4QgzgSOS48N
+dB5oGRzyF8zoRD1Vfq19oh4ReFfb8xKHiFAAWF0FTUUTR6T3VbCuok05Mr26xif7
+b96BWYTTki9CrsvkPZ5RPXxBaslMnSeu77F6mQIDAQABAoIBACF9qHhcj6AypDJ8
+yPVXbLJkME2gEk1B2qD/Etq9k4BhFgMsKNu1T8/gBhh2JQJYt2Kar4OGPulmzKkZ
+rUuEr0+RbqP7OYWZhv9j3pEghEon+Cz7IPr3hC9XG87fIaN7T45NW2PJUWH8zGQh
+qTdUdY6e5wuBoDPSKvXI9wM5KxsBydFNK83nRO/j3dnCKyU6tQPDI2UM/EObbnR/
+upbf8aAv3dcTe5A1o3JW1PuaZynOZ6670h9DwDrUvoyeCyitN6nYfodpgUuVhB4b
+/6LPFAYk5t3BaKpgEq7cZRYECzAAQmiCUvx1c0O2La+8zmVYIKvbVr1ofbMSl4GB
+aR+S/HUCgYEA5t+APdnM/fwmjjkXP/aFVi+BZFzK2CHxKM+zwHaEs635PMYSCPOw
+jNH3UyBs0Dt25IKffL0LziTX7E4d3S1kjVVA700Z4WvtI5cISxCEiG987IyujlW+
+GRVcFFjta+/Pq9tZBZdvyB5dD4KdFJi7wiOoOVdL8nCAYDUuQmkQ9vcCgYEA0BFe
+0x+FFnzu/ciH/M2l1hkTYKEOQiFjc5u97An0k4SQX6hHanIhM9PrE8YGB6pwvb3q
+iODKQeqH623hj51ktpYEyVazabzoyi7SMR0Eo17Ma8XxqkPkcANgW0oz+076swuI
+pt0g4ZtsBPn1c6iIxKutgahbF9tEHiaaUNGP5u8CgYAnsPb4KzCd3bK3MsN3gKdN
+uN8S5Q0OeU9fh83MYdPQnDFELH0/Qr3wh8xwYxux9Pcss/Q9Wrbj2tuwSYHC26H0
+1lpSibvQUGC1hKqtVQqj9h7RACKmX8FNyKlK6jrvGoxmbFBysKVMD0RKfVcKCnQF
+HWI3cXWiGYtIfvwT+MIs4wKBgGE5vLWq5OW9O3AbZHAdKIEC9qsQlzkPoQlEnVrO
+zBqit1P7rmNvMVSmkhKLVgX+/u+q7w1G/TwP+7NnhR82zPkE4Pe0K+IdBCp19oI8
+rQ4qHhu+Q07sqrIs4bnG0sN8BGnl2dUfP14s4izxIGELP92m20za/rYHH613HGTn
+h/bBAoGBAL5u/pKnHA8iC3wFpxNRayi2jzG0gUyHEWZ+zs9C+JtkJIkUPB6reWFr
+iokg053PKHbdXMLElDoTlOV39QVpUTkUop7Qew7ZJPDM9Wzfdrx+c1a7BkeXK8vA
+0DF5EDq/Ru7laLTThEKXoyM844RIEih993m3VjSAnumHygWw5CfP
+-----END RSA PRIVATE KEY-----
diff --git a/vendor/lightsaml/lightsaml/web/sp/sessions.php b/vendor/lightsaml/lightsaml/web/sp/sessions.php
new file mode 100644
index 0000000000..b23bcd08ee
--- /dev/null
+++ b/vendor/lightsaml/lightsaml/web/sp/sessions.php
@@ -0,0 +1,24 @@
+getBuildContainer();
+
+$ssoState = $buildContainer->getStoreContainer()->getSsoStateStore()->get();
+
+foreach ($ssoState->getSsoSessions() as $ssoSession) {
+    print "
    \n"; + print "
  • IDP: ".$ssoSession->getIdpEntityId()."
  • \n"; + print "
  • SP: ".$ssoSession->getSpEntityId()."
  • \n"; + print "
  • NameID: ".$ssoSession->getNameId()."
  • \n"; + print "
  • NameIDFormat: ".$ssoSession->getNameIdFormat()."
  • \n"; + print "
  • SessionIndex: ".$ssoSession->getSessionIndex()."
  • \n"; + print "
  • AuthnInstant: ".$ssoSession->getSessionInstant()->format('Y-m-d H:i:s P')."
  • \n"; + print "
  • FirstAuthOn: ".$ssoSession->getFirstAuthOn()->format('Y-m-d H:i:s P')."
  • \n"; + print "
  • LastAuthOn: ".$ssoSession->getLastAuthOn()->format('Y-m-d H:i:s P')."
  • \n"; + print "
\n"; +} + +if (empty($ssoState->getSsoSessions())) { + print "

No sessions established

\n"; +} diff --git a/vendor/lightsaml/lightsaml/web/sp/testshib-providers.xml b/vendor/lightsaml/lightsaml/web/sp/testshib-providers.xml new file mode 100644 index 0000000000..381cb9c1eb --- /dev/null +++ b/vendor/lightsaml/lightsaml/web/sp/testshib-providers.xml @@ -0,0 +1,380 @@ + + + + + + + + + + + + + + + + + + + + + testshib.org + + TestShib Test IdP + TestShib IdP. Use this as a source of attributes + for your test SP. + https://www.testshib.org/testshibtwo.jpg + + + + + + + + + + + MIIDAzCCAeugAwIBAgIVAPX0G6LuoXnKS0Muei006mVSBXbvMA0GCSqGSIb3DQEB + CwUAMBsxGTAXBgNVBAMMEGlkcC50ZXN0c2hpYi5vcmcwHhcNMTYwODIzMjEyMDU0 + WhcNMzYwODIzMjEyMDU0WjAbMRkwFwYDVQQDDBBpZHAudGVzdHNoaWIub3JnMIIB + IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg9C4J2DiRTEhJAWzPt1S3ryh + m3M2P3hPpwJwvt2q948vdTUxhhvNMuc3M3S4WNh6JYBs53R+YmjqJAII4ShMGNEm + lGnSVfHorex7IxikpuDPKV3SNf28mCAZbQrX+hWA+ann/uifVzqXktOjs6DdzdBn + xoVhniXgC8WCJwKcx6JO/hHsH1rG/0DSDeZFpTTcZHj4S9MlLNUtt5JxRzV/MmmB + 3ObaX0CMqsSWUOQeE4nylSlp5RWHCnx70cs9kwz5WrflnbnzCeHU2sdbNotBEeTH + ot6a2cj/pXlRJIgPsrL/4VSicPZcGYMJMPoLTJ8mdy6mpR6nbCmP7dVbCIm/DQID + AQABoz4wPDAdBgNVHQ4EFgQUUfaDa2mPi24x09yWp1OFXmZ2GPswGwYDVR0RBBQw + EoIQaWRwLnRlc3RzaGliLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEASKKgqTxhqBzR + OZ1eVy++si+eTTUQZU4+8UywSKLia2RattaAPMAcXUjO+3cYOQXLVASdlJtt+8QP + dRkfp8SiJemHPXC8BES83pogJPYEGJsKo19l4XFJHPnPy+Dsn3mlJyOfAa8RyWBS + 80u5lrvAcr2TJXt9fXgkYs7BOCigxtZoR8flceGRlAZ4p5FPPxQR6NDYb645jtOT + MVr3zgfjP6Wh2dt+2p04LG7ENJn8/gEwtXVuXCsPoSCDx9Y0QmyXTJNdV1aB0AhO + RkWPlFYwp+zOyOIR+3m1+pqWFpn0eT/HrxpdKa74FA3R2kq4R7dXe4G0kUgXTdqX + MLRKhDgdmA== + + + + + + + + + + + + + + + urn:mace:shibboleth:1.0:nameIdentifier + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + + + + + + + + + + + + + + + + + + MIIDAzCCAeugAwIBAgIVAPX0G6LuoXnKS0Muei006mVSBXbvMA0GCSqGSIb3DQEB + CwUAMBsxGTAXBgNVBAMMEGlkcC50ZXN0c2hpYi5vcmcwHhcNMTYwODIzMjEyMDU0 + WhcNMzYwODIzMjEyMDU0WjAbMRkwFwYDVQQDDBBpZHAudGVzdHNoaWIub3JnMIIB + IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg9C4J2DiRTEhJAWzPt1S3ryh + m3M2P3hPpwJwvt2q948vdTUxhhvNMuc3M3S4WNh6JYBs53R+YmjqJAII4ShMGNEm + lGnSVfHorex7IxikpuDPKV3SNf28mCAZbQrX+hWA+ann/uifVzqXktOjs6DdzdBn + xoVhniXgC8WCJwKcx6JO/hHsH1rG/0DSDeZFpTTcZHj4S9MlLNUtt5JxRzV/MmmB + 3ObaX0CMqsSWUOQeE4nylSlp5RWHCnx70cs9kwz5WrflnbnzCeHU2sdbNotBEeTH + ot6a2cj/pXlRJIgPsrL/4VSicPZcGYMJMPoLTJ8mdy6mpR6nbCmP7dVbCIm/DQID + AQABoz4wPDAdBgNVHQ4EFgQUUfaDa2mPi24x09yWp1OFXmZ2GPswGwYDVR0RBBQw + EoIQaWRwLnRlc3RzaGliLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEASKKgqTxhqBzR + OZ1eVy++si+eTTUQZU4+8UywSKLia2RattaAPMAcXUjO+3cYOQXLVASdlJtt+8QP + dRkfp8SiJemHPXC8BES83pogJPYEGJsKo19l4XFJHPnPy+Dsn3mlJyOfAa8RyWBS + 80u5lrvAcr2TJXt9fXgkYs7BOCigxtZoR8flceGRlAZ4p5FPPxQR6NDYb645jtOT + MVr3zgfjP6Wh2dt+2p04LG7ENJn8/gEwtXVuXCsPoSCDx9Y0QmyXTJNdV1aB0AhO + RkWPlFYwp+zOyOIR+3m1+pqWFpn0eT/HrxpdKa74FA3R2kq4R7dXe4G0kUgXTdqX + MLRKhDgdmA== + + + + + + + + + + + + + + + urn:mace:shibboleth:1.0:nameIdentifier + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + + + + + TestShib Two Identity Provider + TestShib Two + http://www.testshib.org/testshib-two/ + + + Nate + Klingenstein + ndk@internet2.edu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TestShib Test SP + TestShib SP. Log into this to test your machine. + Once logged in check that all attributes that you expected have been + released. + https://www.testshib.org/testshibtwo.jpg + + + + + + + + MIIEPjCCAyagAwIBAgIBADANBgkqhkiG9w0BAQUFADB3MQswCQYDVQQGEwJVUzEV + MBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMSIwIAYD + VQQKExlUZXN0U2hpYiBTZXJ2aWNlIFByb3ZpZGVyMRgwFgYDVQQDEw9zcC50ZXN0 + c2hpYi5vcmcwHhcNMDYwODMwMjEyNDM5WhcNMTYwODI3MjEyNDM5WjB3MQswCQYD + VQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1 + cmdoMSIwIAYDVQQKExlUZXN0U2hpYiBTZXJ2aWNlIFByb3ZpZGVyMRgwFgYDVQQD + Ew9zcC50ZXN0c2hpYi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB + AQDJyR6ZP6MXkQ9z6RRziT0AuCabDd3x1m7nLO9ZRPbr0v1LsU+nnC363jO8nGEq + sqkgiZ/bSsO5lvjEt4ehff57ERio2Qk9cYw8XCgmYccVXKH9M+QVO1MQwErNobWb + AjiVkuhWcwLWQwTDBowfKXI87SA7KR7sFUymNx5z1aoRvk3GM++tiPY6u4shy8c7 + vpWbVfisfTfvef/y+galxjPUQYHmegu7vCbjYP3On0V7/Ivzr+r2aPhp8egxt00Q + XpilNai12LBYV3Nv/lMsUzBeB7+CdXRVjZOHGuQ8mGqEbsj8MBXvcxIKbcpeK5Zi + JCVXPfarzuriM1G5y5QkKW+LAgMBAAGjgdQwgdEwHQYDVR0OBBYEFKB6wPDxwYrY + StNjU5P4b4AjBVQVMIGhBgNVHSMEgZkwgZaAFKB6wPDxwYrYStNjU5P4b4AjBVQV + oXukeTB3MQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYD + VQQHEwpQaXR0c2J1cmdoMSIwIAYDVQQKExlUZXN0U2hpYiBTZXJ2aWNlIFByb3Zp + ZGVyMRgwFgYDVQQDEw9zcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zAN + BgkqhkiG9w0BAQUFAAOCAQEAc06Kgt7ZP6g2TIZgMbFxg6vKwvDL0+2dzF11Onpl + 5sbtkPaNIcj24lQ4vajCrrGKdzHXo9m54BzrdRJ7xDYtw0dbu37l1IZVmiZr12eE + Iay/5YMU+aWP1z70h867ZQ7/7Y4HW345rdiS6EW663oH732wSYNt9kr7/0Uer3KD + 9CuPuOidBacospDaFyfsaJruE99Kd6Eu/w5KLAGG+m0iqENCziDGzVA47TngKz2v + PVA+aokoOyoz3b53qeti77ijatSEoKjxheBWpO+eoJeGq/e49Um3M2ogIX/JAlMa + Inh+vYSYngQB2sx9LGkR9KHaMKNIGCDehk93Xla4pWJx1w== + + + + + + + + + + + + + + + + + + + + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + urn:mace:shibboleth:1.0:nameIdentifier + + + + + + + + + + + + + + + + + + + + TestShib Two Service Provider + TestShib Two + http://www.testshib.org/testshib-two/ + + + Nate + Klingenstein + ndk@internet2.edu + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/envelope.xsd b/vendor/lightsaml/lightsaml/xsd/envelope.xsd new file mode 100644 index 0000000000..b1a20e08ed --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/envelope.xsd @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Prose in the spec does not specify that attributes are allowed on the Body element + + + + + + + + + + + + + + + + + + + + 'encodingStyle' indicates any canonicalization conventions followed in the contents of the containing element. For example, the value 'http://schemas.xmlsoap.org/soap/encoding/' indicates the pattern described in SOAP specification + + + + + + + + + + + + + + + Fault reporting structure + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-assertion-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-assertion-2.0.xsd new file mode 100644 index 0000000000..478ddfa9ad --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-assertion-2.0.xsd @@ -0,0 +1,283 @@ + + + + + + + Document identifier: saml-schema-assertion-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V1.0 (November, 2002): + Initial Standard Schema. + V1.1 (September, 2003): + Updates within the same V1.0 namespace. + V2.0 (March, 2005): + New assertion schema for SAML V2.0 namespace. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-2.0.xsd new file mode 100644 index 0000000000..5723bb91b8 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-2.0.xsd @@ -0,0 +1,23 @@ + + + + + + Document identifier: saml-schema-authn-context-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New core authentication context schema for SAML V2.0. + This is just an include of all types from the schema + referred to in the include statement below. + + + + + + \ No newline at end of file diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-auth-telephony-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-auth-telephony-2.0.xsd new file mode 100644 index 0000000000..84dc4ec13f --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-auth-telephony-2.0.xsd @@ -0,0 +1,81 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:AuthenticatedTelephony + Document identifier: saml-schema-authn-context-auth-telephony-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-ip-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-ip-2.0.xsd new file mode 100644 index 0000000000..add9e0cc0d --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-ip-2.0.xsd @@ -0,0 +1,65 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol + Document identifier: saml-schema-authn-context-ip-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-ippword-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-ippword-2.0.xsd new file mode 100644 index 0000000000..7496a0464c --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-ippword-2.0.xsd @@ -0,0 +1,67 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocolPassword + Document identifier: saml-schema-authn-context-ippword-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-kerberos-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-kerberos-2.0.xsd new file mode 100644 index 0000000000..88398cf042 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-kerberos-2.0.xsd @@ -0,0 +1,83 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos + Document identifier: saml-schema-authn-context-kerberos-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-mobileonefactor-reg-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-mobileonefactor-reg-2.0.xsd new file mode 100644 index 0000000000..745a277c6a --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-mobileonefactor-reg-2.0.xsd @@ -0,0 +1,186 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:MobileOneFactorContract + Document identifier: saml-schema-authn-context-mobileonefactor-reg-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-mobileonefactor-unreg-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-mobileonefactor-unreg-2.0.xsd new file mode 100644 index 0000000000..deea996615 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-mobileonefactor-unreg-2.0.xsd @@ -0,0 +1,183 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:MobileOneFactorUnregistered + Document identifier: saml-schema-authn-context-mobileonefactor-unreg-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-mobiletwofactor-reg-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-mobiletwofactor-reg-2.0.xsd new file mode 100644 index 0000000000..3bfa7c5c0d --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-mobiletwofactor-reg-2.0.xsd @@ -0,0 +1,202 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:MobileTwoFactorContract + Document identifier: saml-schema-authn-context-mobiletwofactor-reg-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-mobiletwofactor-unreg-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-mobiletwofactor-unreg-2.0.xsd new file mode 100644 index 0000000000..714e0fd93b --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-mobiletwofactor-unreg-2.0.xsd @@ -0,0 +1,200 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:MobileTwoFactorUnregistered + Document identifier: saml-schema-authn-context-mobiletwofactor-unreg-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-nomad-telephony-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-nomad-telephony-2.0.xsd new file mode 100644 index 0000000000..c9065145d7 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-nomad-telephony-2.0.xsd @@ -0,0 +1,81 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:NomadTelephony + Document identifier: saml-schema-authn-context-nomad-telephony-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-personal-telephony-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-personal-telephony-2.0.xsd new file mode 100644 index 0000000000..bdb0f72b1a --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-personal-telephony-2.0.xsd @@ -0,0 +1,80 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:PersonalizedTelephony + Document identifier: saml-schema-authn-context-personal-telephony-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-pgp-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-pgp-2.0.xsd new file mode 100644 index 0000000000..cbff52aeae --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-pgp-2.0.xsd @@ -0,0 +1,83 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:PGP + Document identifier: saml-schema-authn-context-pgp-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-ppt-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-ppt-2.0.xsd new file mode 100644 index 0000000000..a0d9bcb63d --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-ppt-2.0.xsd @@ -0,0 +1,81 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport + Document identifier: saml-schema-authn-context-ppt-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-pword-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-pword-2.0.xsd new file mode 100644 index 0000000000..7c98cdd203 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-pword-2.0.xsd @@ -0,0 +1,64 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:Password + Document identifier: saml-schema-authn-context-pword-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-session-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-session-2.0.xsd new file mode 100644 index 0000000000..c7340bf809 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-session-2.0.xsd @@ -0,0 +1,64 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession + Document identifier: saml-schema-authn-context-session-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-smartcard-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-smartcard-2.0.xsd new file mode 100644 index 0000000000..64a7479a0e --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-smartcard-2.0.xsd @@ -0,0 +1,64 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:Smartcard + Document identifier: saml-schema-authn-context-smartcard-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-smartcardpki-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-smartcardpki-2.0.xsd new file mode 100644 index 0000000000..bb6474d4cf --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-smartcardpki-2.0.xsd @@ -0,0 +1,129 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:SmartcardPKI + Document identifier: saml-schema-authn-context-smartcardpki-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-softwarepki-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-softwarepki-2.0.xsd new file mode 100644 index 0000000000..2054a816a8 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-softwarepki-2.0.xsd @@ -0,0 +1,129 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:SoftwarePKI + Document identifier: saml-schema-authn-context-softwarepki-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-spki-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-spki-2.0.xsd new file mode 100644 index 0000000000..698c7c6cff --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-spki-2.0.xsd @@ -0,0 +1,83 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:SPKI + Document identifier: saml-schema-authn-context-spki-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-srp-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-srp-2.0.xsd new file mode 100644 index 0000000000..07c6ae4792 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-srp-2.0.xsd @@ -0,0 +1,82 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:SecureRemotePassword + Document identifier: saml-schema-authn-context-srp-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-sslcert-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-sslcert-2.0.xsd new file mode 100644 index 0000000000..88a4f17834 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-sslcert-2.0.xsd @@ -0,0 +1,97 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient + Document identifier: saml-schema-authn-context-sslcert-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-telephony-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-telephony-2.0.xsd new file mode 100644 index 0000000000..e4906c5251 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-telephony-2.0.xsd @@ -0,0 +1,79 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:Telephony + Document identifier: saml-schema-authn-context-telephony-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-timesync-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-timesync-2.0.xsd new file mode 100644 index 0000000000..53b425fc8c --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-timesync-2.0.xsd @@ -0,0 +1,105 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:TimeSyncToken + Document identifier: saml-schema-authn-context-timesync-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-types-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-types-2.0.xsd new file mode 100644 index 0000000000..6ae1875e4b --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-types-2.0.xsd @@ -0,0 +1,821 @@ + + + + + + Document identifier: saml-schema-authn-context-types-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New core authentication context schema types for SAML V2.0. + + + + + + + A particular assertion on an identity + provider's part with respect to the authentication + context associated with an authentication assertion. + + + + + + + + Refers to those characteristics that describe the + processes and mechanisms + the Authentication Authority uses to initially create + an association between a Principal + and the identity (or name) by which the Principal will + be known + + + + + + + + This element indicates that identification has been + performed in a physical + face-to-face meeting with the principal and not in an + online manner. + + + + + + + + + + + + + + + + + + + + Refers to those characterstics that describe how the + 'secret' (the knowledge or possession + of which allows the Principal to authenticate to the + Authentication Authority) is kept secure + + + + + + + + This element indicates the types and strengths of + facilities + of a UA used to protect a shared secret key from + unauthorized access and/or use. + + + + + + + + This element indicates the types and strengths of + facilities + of a UA used to protect a private key from + unauthorized access and/or use. + + + + + + + The actions that must be performed + before the private key can be used. + + + + + + Whether or not the private key is shared + with the certificate authority. + + + + + + + In which medium is the key stored. + memory - the key is stored in memory. + smartcard - the key is stored in a smartcard. + token - the key is stored in a hardware token. + MobileDevice - the key is stored in a mobile device. + MobileAuthCard - the key is stored in a mobile + authentication card. + + + + + + + + + + + This element indicates that a password (or passphrase) + has been used to + authenticate the Principal to a remote system. + + + + + + + + This element indicates that a Pin (Personal + Identification Number) has been used to authenticate the Principal to + some local system in order to activate a key. + + + + + + + + This element indicates that a hardware or software + token is used + as a method of identifying the Principal. + + + + + + + + This element indicates that a time synchronization + token is used to identify the Principal. hardware - + the time synchonization + token has been implemented in hardware. software - the + time synchronization + token has been implemented in software. SeedLength - + the length, in bits, of the + random seed used in the time synchronization token. + + + + + + + + This element indicates that a smartcard is used to + identity the Principal. + + + + + + + + This element indicates the minimum and/or maximum + ASCII length of the password which is enforced (by the UA or the + IdP). In other words, this is the minimum and/or maximum number of + ASCII characters required to represent a valid password. + min - the minimum number of ASCII characters required + in a valid password, as enforced by the UA or the IdP. + max - the maximum number of ASCII characters required + in a valid password, as enforced by the UA or the IdP. + + + + + + + + This element indicates the length of time for which an + PIN-based authentication is valid. + + + + + + + + Indicates whether the password was chosen by the + Principal or auto-supplied by the Authentication Authority. + principalchosen - the Principal is allowed to choose + the value of the password. This is true even if + the initial password is chosen at random by the UA or + the IdP and the Principal is then free to change + the password. + automatic - the password is chosen by the UA or the + IdP to be cryptographically strong in some sense, + or to satisfy certain password rules, and that the + Principal is not free to change it or to choose a new password. + + + + + + + + + + + + + + + + + + + Refers to those characteristics that define the + mechanisms by which the Principal authenticates to the Authentication + Authority. + + + + + + + + The method that a Principal employs to perform + authentication to local system components. + + + + + + + + The method applied to validate a principal's + authentication across a network + + + + + + + + Supports Authenticators with nested combinations of + additional complexity. + + + + + + + + Indicates that the Principal has been strongly + authenticated in a previous session during which the IdP has set a + cookie in the UA. During the present session the Principal has only + been authenticated by the UA returning the cookie to the IdP. + + + + + + + + Rather like PreviousSession but using stronger + security. A secret that was established in a previous session with + the Authentication Authority has been cached by the local system and + is now re-used (e.g. a Master Secret is used to derive new session + keys in TLS, SSL, WTLS). + + + + + + + + This element indicates that the Principal has been + authenticated by a zero knowledge technique as specified in ISO/IEC + 9798-5. + + + + + + + + + + This element indicates that the Principal has been + authenticated by a challenge-response protocol utilizing shared secret + keys and symmetric cryptography. + + + + + + + + + + + + This element indicates that the Principal has been + authenticated by a mechanism which involves the Principal computing a + digital signature over at least challenge data provided by the IdP. + + + + + + + + The local system has a private key but it is used + in decryption mode, rather than signature mode. For example, the + Authentication Authority generates a secret and encrypts it using the + local system's public key: the local system then proves it has + decrypted the secret. + + + + + + + + The local system has a private key and uses it for + shared secret key agreement with the Authentication Authority (e.g. + via Diffie Helman). + + + + + + + + + + + + + + + This element indicates that the Principal has been + authenticated through connection from a particular IP address. + + + + + + + + The local system and Authentication Authority + share a secret key. The local system uses this to encrypt a + randomised string to pass to the Authentication Authority. + + + + + + + + The protocol across which Authenticator information is + transferred to an Authentication Authority verifier. + + + + + + + + This element indicates that the Authenticator has been + transmitted using bare HTTP utilizing no additional security + protocols. + + + + + + + + This element indicates that the Authenticator has been + transmitted using a transport mechanism protected by an IPSEC session. + + + + + + + + This element indicates that the Authenticator has been + transmitted using a transport mechanism protected by a WTLS session. + + + + + + + + This element indicates that the Authenticator has been + transmitted solely across a mobile network using no additional + security mechanism. + + + + + + + + + + + This element indicates that the Authenticator has been + transmitted using a transport mechnanism protected by an SSL or TLS + session. + + + + + + + + + + + + Refers to those characteristics that describe + procedural security controls employed by the Authentication Authority. + + + + + + + + + + + + Provides a mechanism for linking to external (likely + human readable) documents in which additional business agreements, + (e.g. liability constraints, obligations, etc) can be placed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This attribute indicates whether or not the + Identification mechanisms allow the actions of the Principal to be + linked to an actual end user. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This element indicates that the Key Activation Limit is + defined as a specific duration of time. + + + + + + + + This element indicates that the Key Activation Limit is + defined as a number of usages. + + + + + + + + This element indicates that the Key Activation Limit is + the session. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-x509-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-x509-2.0.xsd new file mode 100644 index 0000000000..7ea725f508 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-x509-2.0.xsd @@ -0,0 +1,83 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:X509 + Document identifier: saml-schema-authn-context-x509-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-xmldsig-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-xmldsig-2.0.xsd new file mode 100644 index 0000000000..2616411f43 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-authn-context-xmldsig-2.0.xsd @@ -0,0 +1,83 @@ + + + + + + + + + Class identifier: urn:oasis:names:tc:SAML:2.0:ac:classes:XMLDSig + Document identifier: saml-schema-authn-context-xmldsig-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New authentication context class schema for SAML V2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-dce-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-dce-2.0.xsd new file mode 100644 index 0000000000..719dfe9ec7 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-dce-2.0.xsd @@ -0,0 +1,29 @@ + + + + + Document identifier: saml-schema-dce-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + Custom schema for DCE attribute profile, first published in SAML 2.0. + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-ecp-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-ecp-2.0.xsd new file mode 100644 index 0000000000..571abd00f1 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-ecp-2.0.xsd @@ -0,0 +1,57 @@ + + + + + + + + Document identifier: saml-schema-ecp-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + Custom schema for ECP profile, first published in SAML 2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-metadata-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-metadata-2.0.xsd new file mode 100644 index 0000000000..f48c352c2e --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-metadata-2.0.xsd @@ -0,0 +1,337 @@ + + + + + + + + + Document identifier: saml-schema-metadata-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + Schema for SAML metadata, first published in SAML 2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-protocol-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-protocol-2.0.xsd new file mode 100644 index 0000000000..e6be1e4b5e --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-protocol-2.0.xsd @@ -0,0 +1,302 @@ + + + + + + + Document identifier: saml-schema-protocol-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V1.0 (November, 2002): + Initial Standard Schema. + V1.1 (September, 2003): + Updates within the same V1.0 namespace. + V2.0 (March, 2005): + New protocol schema based in a SAML V2.0 namespace. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-x500-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-x500-2.0.xsd new file mode 100644 index 0000000000..141b634519 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-x500-2.0.xsd @@ -0,0 +1,20 @@ + + + + + Document identifier: saml-schema-x500-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + Custom schema for X.500 attribute profile, first published in SAML 2.0. + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/saml-schema-xacml-2.0.xsd b/vendor/lightsaml/lightsaml/xsd/saml-schema-xacml-2.0.xsd new file mode 100644 index 0000000000..a83bc02075 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/saml-schema-xacml-2.0.xsd @@ -0,0 +1,19 @@ + + + + + Document identifier: saml-schema-xacml-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + Custom schema for XACML attribute profile, first published in SAML 2.0. + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/xenc-schema.xsd b/vendor/lightsaml/lightsaml/xsd/xenc-schema.xsd new file mode 100644 index 0000000000..d61229fd50 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/xenc-schema.xsd @@ -0,0 +1,146 @@ + + + + + + ]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/lightsaml/lightsaml/xsd/xml.xsd b/vendor/lightsaml/lightsaml/xsd/xml.xsd new file mode 100644 index 0000000000..aea7d0db0a --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/xml.xsd @@ -0,0 +1,287 @@ + + + + + + +
+

About the XML namespace

+ +
+

+ This schema document describes the XML namespace, in a form + suitable for import by other schema documents. +

+

+ See + http://www.w3.org/XML/1998/namespace.html and + + http://www.w3.org/TR/REC-xml for information + about this namespace. +

+

+ Note that local names in this namespace are intended to be + defined only by the World Wide Web Consortium or its subgroups. + The names currently defined in this namespace are listed below. + They should not be used with conflicting semantics by any Working + Group, specification, or document instance. +

+

+ See further below in this document for more information about how to refer to this schema document from your own + XSD schema documents and about the + namespace-versioning policy governing this schema document. +

+
+
+
+
+ + + + +
+ +

lang (as an attribute name)

+

+ denotes an attribute whose value + is a language code for the natural language of the content of + any element; its value is inherited. This name is reserved + by virtue of its definition in the XML specification.

+ +
+
+

Notes

+

+ Attempting to install the relevant ISO 2- and 3-letter + codes as the enumerated possible values is probably never + going to be a realistic possibility. +

+

+ See BCP 47 at + http://www.rfc-editor.org/rfc/bcp/bcp47.txt + and the IANA language subtag registry at + + http://www.iana.org/assignments/language-subtag-registry + for further information. +

+

+ The union allows for the 'un-declaration' of xml:lang with + the empty string. +

+
+
+
+ + + + + + + + + +
+ + + + +
+ +

space (as an attribute name)

+

+ denotes an attribute whose + value is a keyword indicating what whitespace processing + discipline is intended for the content of the element; its + value is inherited. This name is reserved by virtue of its + definition in the XML specification.

+ +
+
+
+ + + + + + +
+ + + +
+ +

base (as an attribute name)

+

+ denotes an attribute whose value + provides a URI to be used as the base for interpreting any + relative URIs in the scope of the element on which it + appears; its value is inherited. This name is reserved + by virtue of its definition in the XML Base specification.

+ +

+ See http://www.w3.org/TR/xmlbase/ + for information about this attribute. +

+
+
+
+
+ + + + +
+ +

id (as an attribute name)

+

+ denotes an attribute whose value + should be interpreted as if declared to be of type ID. + This name is reserved by virtue of its definition in the + xml:id specification.

+ +

+ See http://www.w3.org/TR/xml-id/ + for information about this attribute. +

+
+
+
+
+ + + + + + + + + + +
+ +

Father (in any context at all)

+ +
+

+ denotes Jon Bosak, the chair of + the original XML Working Group. This name is reserved by + the following decision of the W3C XML Plenary and + XML Coordination groups: +

+
+

+ In appreciation for his vision, leadership and + dedication the W3C XML Plenary on this 10th day of + February, 2000, reserves for Jon Bosak in perpetuity + the XML name "xml:Father". +

+
+
+
+
+
+ + + +
+

About this schema document

+ +
+

+ This schema defines attributes and an attribute group suitable + for use by schemas wishing to allow xml:base, + xml:lang, xml:space or + xml:id attributes on elements they define. +

+

+ To enable this, such a schema must import this schema for + the XML namespace, e.g. as follows: +

+
+          <schema . . .>
+           . . .
+           <import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+     
+

+ or +

+
+           <import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+     
+

+ Subsequently, qualified reference to any of the attributes or the + group defined below will have the desired effect, e.g. +

+
+          <type . . .>
+           . . .
+           <attributeGroup ref="xml:specialAttrs"/>
+     
+

+ will define a type which will schema-validate an instance element + with any of those attributes. +

+
+
+
+
+ + + +
+

Versioning policy for this schema document

+
+

+ In keeping with the XML Schema WG's standard versioning + policy, this schema document will persist at + + http://www.w3.org/2009/01/xml.xsd. +

+

+ At the date of issue it can also be found at + + http://www.w3.org/2001/xml.xsd. +

+

+ The schema document at that URI may however change in the future, + in order to remain compatible with the latest version of XML + Schema itself, or with the XML namespace itself. In other words, + if the XML Schema or XML namespaces change, the version of this + document at + http://www.w3.org/2001/xml.xsd + + will change accordingly; the version at + + http://www.w3.org/2009/01/xml.xsd + + will not change. +

+

+ Previous dated (and unchanging) versions of this schema + document are at: +

+ +
+
+
+
+ +
+ diff --git a/vendor/lightsaml/lightsaml/xsd/xmldsig-core-schema.xsd b/vendor/lightsaml/lightsaml/xsd/xmldsig-core-schema.xsd new file mode 100644 index 0000000000..df126b30e6 --- /dev/null +++ b/vendor/lightsaml/lightsaml/xsd/xmldsig-core-schema.xsd @@ -0,0 +1,318 @@ + + + + + + ]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/onelogin/php-saml/CHANGELOG b/vendor/onelogin/php-saml/CHANGELOG new file mode 100644 index 0000000000..28c9c74fc0 --- /dev/null +++ b/vendor/onelogin/php-saml/CHANGELOG @@ -0,0 +1,273 @@ +CHANGELOG +========= +v.3.3.1 +* Update xmlseclibs to 3.0.4 +* Remove Comparison atribute from RequestedAuthnContext when setting has empty value + +v.3.3.0 +* Set true as the default value for strict setting +* Relax comparision of false on SignMetadata +* Fix CI + +v.3.2.1 +* Add missed nameIdValueReq parameter to buildAuthnRequest method + +v.3.2.0 +* Add support for Subjects on AuthNRequests by the new parameter nameIdValueReq +* Support SLO ResponseLocation +* [#344](https://github.com/onelogin/php-saml/issues/344) Raise errors on IdPMetadataParser::parseRemoteXML and IdPMetadataParser::parseFileXML +* [#356](https://github.com/onelogin/php-saml/issues/356) Support 'x509cert' and 'privateKey' on signMetadata security setting + +v.3.1.1 +* Force to use at least xmlseclibs 3.0.3 for security reasons +* [#367](https://github.com/onelogin/php-saml/pull/367) Move the creation of the AuthnRequest to separate function +* Set strict=true on config examples +* Move phpunit.xml + +v.3.1.0 +* Security improvement suggested by Nils Engelbertz to prevent DDOS by expansion of internally defined entities (XEE) +* Fix setting_example.php servicename parameter + +v.3.0.0 +* Remove mcrypt dependency. Compatible with PHP 7.2 +* xmlseclibs now is not part of the toolkit and need to be installed from original source + +v.2.17.1 +* Update xmlseclibs to 3.0.4 +* Remove Comparison atribute from RequestedAuthnContext when setting has empty value + +v.2.17.0 +* Set true as the default value for strict setting +* Support 'x509cert' and 'privateKey' on signMetadata security settings +* Relax comparision of false on SignMetadata +* Fix CI + +v.2.16.0 +* Support SLO ResponseLocation +* [#344](https://github.com/onelogin/php-saml/issues/344) Raise errors on IdPMetadataParser::parseRemoteXML and IdPMetadataParser::parseFileXML +* Adjusted acs endpoint to extract NameQualifier and SPNameQualifier from SAMLResponse. Adjusted single logout service to provide NameQualifier and SPNameQualifier to logout method. Add getNameIdNameQualifier to Auth and SamlResponse. Extend logout method from Auth and LogoutRequest constructor to support SPNameQualifier parameter. Align LogoutRequest constructor with SAML specs +* Add support for Subjects on AuthNRequests by the new parameter +* Set strict=true on config examples + +v.2.15.0 +* Security improvement suggested by Nils Engelbertz to prevent DDOS by expansion of internally defined entities (XEE) +* Fix bug on settings_example.php + +v.2.14.0 +* Add parameter to the decryptElement method to make optional the formatting +* [#283](https://github.com/onelogin/php-saml/pull/283) New method of importing a decrypted assertion into the XML document to replace the EncryptedAssertion. Fix signature issues on Signed Encrypted Assertions with default namespace +* Allow the getSPMetadata() method to always include the encryption Key Descriptor +* Change some Fatal Error to Exceptions +* [#265](https://github.com/onelogin/php-saml/issues/265) Support parameters at getSPMetadata method +* Avoid calling static method using this + +v.2.13.0 +* Update xmlseclibs with some fixes. +* Add extra protection verifying the Signature algorithm used on SignedInfo element, not only rely on the xmlseclibs verify / verifySignature methods. +* Add getAttributesWithFriendlyName method which returns the set of SAML attributes indexed by FriendlyName +* Fix bug on parseRemoteXML and parseFileXML. Internal calls to parseXML missed the desiredNameIdFormat parameter + +v.2.12.0 +* Improve Time management. Use DateTime/DateTimeZone classes. +* Escape error messages in debug mode +* Improve phpdoc +* Add an extra filter to the url to be used on redirection + +* [#242](https://github.com/onelogin/php-saml/pull/242) Document that SHA-1 must not be used +* [#250](https://github.com/onelogin/php-saml/pull/250) Fixed issue with IdPMetadataParser only keeping 1 certificate when multiple certificates of a single type were provided. +* [#263](https://github.com/onelogin/php-saml/issues/263) Fix incompatibility with ADFS on SLO. When on php saml settings NameID Format is set as unspecified but the SAMLResponse has no NameID Format, no NameID Format should be specified on LogoutRequest. + +v.2.11.0 +* [#236](https://github.com/onelogin/php-saml/pull/236) Exclude unnecesary files from Composer production downloads +* [#226](https://github.com/onelogin/php-saml/pull/226) Add possibility to handle nameId NameQualifier attribute in SLO Request +* Improve logout documentation on Readme. +* Improve multi-certificate support + +v.2.10.7 +* Fix IdPMetadataParser. The SingleLogoutService retrieved method was wrong +* [#201](https://github.com/onelogin/php-saml/issues/201) Fix issues with SP entity_id, acs url and sls url that contains & + +v.2.10.6 +* [#206](https://github.com/onelogin/php-saml/pull/206)Be able to register future SP x509cert on the settings and publish it on SP metadata +* [#206](https://github.com/onelogin/php-saml/pull/206) Be able to register more than 1 Identity Provider x509cert, linked with an specific use (signing or encryption) +* [#206](https://github.com/onelogin/php-saml/pull/206) Support the ability to parse IdP XML metadata (remote url or file) and be able to inject the data obtained on the settings. + +v.2.10.5 +* Be able to get at the auth object the last processed ID +* Improve NameID Format support +* Reset errorReason attribute of the auth object after each Process method +* Validate serial number as string to work around libxml2 limitation +* Make the Issuer on the Response Optional + +v.2.10.4 +* [+](https://github.com/onelogin/php-saml/commit/949359f5cad5e1d085c4e5447d9aa8f49a6e82a1) Security update for signature validation on LogoutRequest/LogoutResponse +* [#192](https://github.com/onelogin/php-saml/pull/192) Added ability to configure DigestAlgorithm in settings +* [#183](https://github.com/onelogin/php-saml/pull/183) Fix strpos bug when decrypting assertions +* [#186](https://github.com/onelogin/php-saml/pull/186) Improve info on entityId validation Exception +* [#188](https://github.com/onelogin/php-saml/pull/188) Fixed issue with undefined constant of UNEXPECTED_SIGNED_ELEMENT +* Read ACS binding on AuthNRequest builder from settings +* Be able to relax Destination validation on SAMLResponses and let this + attribute to be empty with the 'relaxDestinationValidation' setting + +v.2.10.3 +* Implement a more specific exception class for handling some validation errors +* Minor changes on time validation/exceptions +* Add hooks to retrieve last-sent and last-received requests and responses +* Improve/Fix tests +* Add DigestAlgorithm support on addSign +* [#177](https://github.com/onelogin/php-saml/pull/177) Add error message for bad OneLogin_Saml2_Settings argument + +v.2.10.2 +* [#175](https://github.com/onelogin/php-saml/pull/175) Allow overriding of host, port, protocol and url path for URL building +* [#173](https://github.com/onelogin/php-saml/pull/173) Provide better support to NameIdFormat +* Fix another issue on Assertion Signature validation when the assertion contains no namespace, container has saml2 namespace and it was encrypted + +v.2.10.1 +* Fix error message on SignMetadata process +* Fix issue on Assertion Signature validation when the assertion contains no namespace and it was encrypted + +v.2.10.0 +* Several security improvements: + * Conditions element required and unique. + * AuthnStatement element required and unique. + * SPNameQualifier must math the SP EntityID + * Reject saml:Attribute element with same “Name” attribute + * Reject empty nameID + * Require Issuer element. (Must match IdP EntityID). + * Destination value can't be blank (if present must match ACS URL). + * Check that the EncryptedAssertion element only contains 1 Assertion element. +* Improve Signature validation process +* AttributeConsumingService support +* Support lowercase Urlencoding (ADFS compatibility). +* [#154](https://github.com/onelogin/php-saml/pull/154) getSelfHost no longer returns a port number +* [#156](https://github.com/onelogin/php-saml/pull/156) Use correct host on response destination fallback check +* [#158](https://github.com/onelogin/php-saml/pull/158) NEW Control usage of X-Forwarded-* headers +* Fix issue with buildRequestSignature. Added RelayState to the SignQuery only if is not null. +* Add Signature Wrapping prevention Test +* Improve _decryptAssertion in order to take care of Assertions with problems with namespaces +* Improve documentation + +v.2.9.1 +....... +* [134](https://github.com/onelogin/php-saml/pull/134) PHP7 production settings compiles out assert(), throw an exception explicitly +* [132](https://github.com/onelogin/php-saml/pull/132) Add note for "wantAssertionsEncrypted" +* Update copyright on LICENSE + +v.2.9.0 +------- +* Change the decrypt assertion process. +* Add 2 extra validations to prevent Signature wrapping attacks. +* Remove reference to wrong NameIDFormat: urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified should be urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified +* [128](https://github.com/onelogin/php-saml/pull/128) Test php7 and upgrade phpunit +* Update Readme with more descriptive requestedAuthnContext description and Security Guidelines + +v.2.8.0 +------- +* Make NameIDPolicy of AuthNRequest optional +* Make nameID requirement on SAMLResponse optional +* Fix empty URI support +* Symmetric encryption key support +* Add more Auth Context options to the constant class +* Fix DSA_SHA1 constant on xmlseclibs +* Set none requestedAuthnContext as default behaviour +* Update xmlseclibs lib +* Improve formatPrivateKey method +* Fix bug when signing metadata, the SignatureMethod was not provided +* Fix getter for lastRequestID parameter in OneLogin_Saml2_Auth class +* Add $wantEncrypted parameter on addX509KeyDescriptors method that will allow to set KeyDescriptor[use='encryption'] if wantNameIdEncrypted or wantAssertionsEncrypted enabled +* Add $stay parameter on redirectTo method. (login/logout supports $stay but I forgot add this on previous 2.7.0 version) +* Improve code style + +v.2.7.0 +------- +* Trim acs, slo and issuer urls. +* Fix PHP 7 error (used continue outside a loop/switch). +* Fix bug on organization element of the SP metadata builder. +* Fix typos on documentation. Fix ALOWED Misspell. +* Be able to extract RequestID. Add RequestID validation on demo1. +* Add $stay parameter to login, logout and processSLO method. + +v.2.6.1 +------- +* Fix bug on cacheDuration of the Metadata XML generated. +* Make SPNameQualifier optional on the generateNameId method. Avoid the use of SPNameQualifier when generating the NameID on the LogoutRequest builder. +* Allows the authn comparsion attribute to be set via config. +* Retrieve Session Timeout after processResponse with getSessionExpiration(). +* Improve readme readability. +* Allow single log out to work for applications not leveraging php session_start. Added a callback parameter in order to close the session at processSLO. + +v.2.6.0 +------- +* Set NAMEID_UNSPECIFIED as default NameIDFormat to prevent conflicts with IdPs that don't support NAMEID_PERSISTENT. +* Now the SP is able to select the algorithm to be used on signatures (DSA_SHA1, RSA_SHA1, RSA_SHA256, RSA_SHA384, RSA_SHA512). +* Change visibility of _decryptAssertion to protected. +* Update xmlseclibs library. +* Handle valid but uncommon dsig block with no URI in the reference. +* login, logout and processSLO now return ->redirectTo instead of just call it. +* Split the setting check methods. Now 1 method for IdP settings and other for SP settings. +* Let the setting object to avoid the IdP setting check. required if we want to publish SP SAML Metadata when the IdP data is still not provided. + +v.2.5.0 +------- +* Do accesible the ID of the object Logout Request (id attribute). +* Add note about the fact that PHP 5.3 is unssuported. +* Add fingerprint algorithm support. +* Add dependences to composer. + +v.2.4.0 +------- +* Fix wrong element order in generated metadata. +* Added SLO with nameID and SessionIndex in demo1. +* Improve isHTTPS method in order to support HTTP_X_FORWARDED_PORT. +* Set optional the XMLvalidation (enable/disable it with wantXMLValidation security setting). + +v.2.3.0 +------- +* Resolve namespace problem. Some IdPs uses saml2p:Response and saml2:Assertion instead of samlp:Response saml:Assertion. +* Improve test and documentation. +* Improve ADFS compatibility. +* Remove unnecessary XSDs files. +* Make available the reason for the saml message invalidation. +* Adding ability to set idp cert once the Setting object initialized. +* Fix status info issue. +* Reject SAML Response if not signed and strict = false. +* Support NameId and SessionIndex in LogoutRequest. +* Add ForceAuh and IsPassive support. + +v.2.2.0 +------- +* Fix bug with Encrypted nameID on LogoutRequest. +* Fixed usability bug. SP will inform about AuthFail status after process a Response. +* Added SessionIndex support on LogoutRequest, and know is accesible from the Auth class. +* LogoutRequest and LogoutResponse classes now accept non deflated xml. +* Improved the XML metadata/ Decrypted Assertion output. (prettyprint). +* Fix bug in formatPrivateKey method, the key could be not RSA. +* Explicit warning message for signed element problem. +* Decrypt method improved. +* Support more algorithm at the SigAlg in the Signed LogoutRequests and LogoutResponses +* AuthNRequest now stores ID (it can be retrieved later). +* Fixed a typo on the 'NameIdPolicy' attribute that appeared at the README and settings_example file. + + +v.2.1.0 +------- + +* The isValid method of the Logout Request is now non-static. (affects processSLO method of Auth.php). +* Logout Request constructor now accepts encoded logout requests. +* Now after validate a message, if fails a method getError of the object will return the cause. +* Fix typos. +* Added extra parameters option to login and logout methods. +* Improve Test (new test, use the new getError method for testing). +* Bugfix namespace problem when getting Attributes. + + +v.2.0.0 +------- + +* New PHP SAML Toolkit (SLO, Sign, Encryptation). + + +v.1.0.0 +------- + +* Old PHP SAML Toolkit. diff --git a/vendor/onelogin/php-saml/LICENSE b/vendor/onelogin/php-saml/LICENSE new file mode 100644 index 0000000000..dbbca9c6cb --- /dev/null +++ b/vendor/onelogin/php-saml/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2010-2016 OneLogin, Inc. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + diff --git a/vendor/onelogin/php-saml/README.md b/vendor/onelogin/php-saml/README.md new file mode 100644 index 0000000000..3d1b27b760 --- /dev/null +++ b/vendor/onelogin/php-saml/README.md @@ -0,0 +1,1518 @@ +# OneLogin's SAML PHP Toolkit Compatible with PHP 5.X & 7.X + +[![Build Status](https://api.travis-ci.org/onelogin/php-saml.png?branch=master)](http://travis-ci.org/onelogin/php-saml) [![Coverage Status](https://coveralls.io/repos/onelogin/php-saml/badge.png)](https://coveralls.io/r/onelogin/php-saml) [![License](https://poser.pugx.org/onelogin/php-saml/license.png)](https://packagist.org/packages/onelogin/php-saml) + +Add SAML support to your PHP software using this library. +Forget those complicated libraries and use this open source library provided +and supported by OneLogin Inc. + + +Warning +------- + +Version 3.3.1 updates xmlseclibs to 3.0.4 (CVE-2019-3465), but php-saml was not directly affected since it implements additional checks that prevent to exploit that vulnerability. + +Version 3.3.0 sets strict mode active by default + +Update php-saml to 3.1.0, this version includes a security patch related to XEE attacks. + +This version is compatible with PHP 7.X and does not include xmlseclibs (you will need to install it via composer, dependency described in composer.json) + +Security Guidelines +------------------- + +If you believe you have discovered a security vulnerability in this toolkit, please report it at https://www.onelogin.com/security with a description. We follow responsible disclosure guidelines, and will work with you to quickly find a resolution. + + +Why add SAML support to my software? +------------------------------------ + +SAML is an XML-based standard for web browser single sign-on and is defined by +the OASIS Security Services Technical Committee. The standard has been around +since 2002, but lately it is becoming popular due its advantages: + + * **Usability** - One-click access from portals or intranets, deep linking, + password elimination and automatically renewing sessions make life + easier for the user. + * **Security** - Based on strong digital signatures for authentication and + integrity, SAML is a secure single sign-on protocol that the largest + and most security conscious enterprises in the world rely on. + * **Speed** - SAML is fast. One browser redirect is all it takes to securely + sign a user into an application. + * **Phishing Prevention** - If you don’t have a password for an app, you + can’t be tricked into entering it on a fake login page. + * **IT Friendly** - SAML simplifies life for IT because it centralizes + authentication, provides greater visibility and makes directory + integration easier. + * **Opportunity** - B2B cloud vendor should support SAML to facilitate the + integration of their product. + + +General description +------------------- + +OneLogin's SAML PHP toolkit let you build a SP (Service Provider) over +your PHP application and connect it to any IdP (Identity Provider). + +Supports: + + * SSO and SLO (SP-Initiated and IdP-Initiated). + * Assertion and nameId encryption. + * Assertion signature. + * Message signature: AuthNRequest, LogoutRequest, LogoutResponses. + * Enable an Assertion Consumer Service endpoint. + * Enable a Single Logout Service endpoint. + * Publish the SP metadata (which can be signed). + +Key features: + + * **saml2int** - Implements the SAML 2.0 Web Browser SSO Profile. + * **Session-less** - Forget those common conflicts between the SP and + the final app, the toolkit delegate session in the final app. + * **Easy to use** - Programmer will be allowed to code high-level and + low-level programming, 2 easy to use APIs are available. + * **Tested** - Thoroughly tested. + * **Popular** - OneLogin's customers use it. Many PHP SAML plugins uses it. + +Integrate your PHP toolkit at OneLogin using this guide: [https://developers.onelogin.com/page/saml-toolkit-for-php](https://developers.onelogin.com/page/saml-toolkit-for-php) + +Installation +------------ + +### Dependencies ### + + * `php >= 5.4` and some core extensions like `php-xml`, `php-date`, `php-zlib`. + * `openssl`. Install the openssl library. It handles x509 certificates. + * `gettext`. Install that library and its php driver. It handles translations. + * `curl`. Install that library and its php driver if you plan to use the IdP Metadata parser. + +### Code ### + +#### Option 1. Download from github #### + +The toolkit is hosted on github. You can download it from: + + * https://github.com/onelogin/php-saml/releases + +Search for 3.X.X releases + +Copy the core of the library inside the php application. (each application has its +structure so take your time to locate the PHP SAML toolkit in the best place). +See the "Guide to add SAML support to my app" to know how. + +#### Option 2. Composer #### + +The toolkit supports [composer](https://getcomposer.org/). You can find the `onelogin/php-saml` package at https://packagist.org/packages/onelogin/php-saml + +In order to import the saml toolkit to your current php project, execute +``` +composer require onelogin/php-saml +``` + +Remember to select the 3.X.X branch + +After installation has completed you will find at the `vendor/` folder a new folder named `onelogin` and inside the `php-saml`. Make sure you are including the autoloader provided by composer. It can be found at `vendor/autoload.php`. + +**Important** In this option, the x509 certs must be stored at `vendor/onelogin/php-saml/certs` +and settings file stored at `vendor/onelogin/php-saml`. + +Your settings are at risk of being deleted when updating packages using `composer update` or similar commands. So it is **highly** recommended that instead of using settings files, you pass the settings as an array directly to the constructor (explained later in this document). If you do not use this approach your settings are at risk of being deleted when updating packages using `composer update` or similar commands. + +Compatibility +------------- + +This 3.X.X supports PHP 7.X. but can be used with PHP >=5.4 as well (5.6.24+ recommended for security reasons). + +Namespaces +---------- + +If you are using the library with a framework like Symfony that contains +namespaces, remember that calls to the class must be done by adding a backslash (`\`) to the +start, for example to use the static method getSelfURLNoQuery use: + + \OneLogin\Saml2\Utils::getSelfURLNoQuery() + + +Security warning +---------------- + +In production, the `strict` parameter **MUST** be set as `"true"` and the +`signatureAlgorithm` and `digestAlgorithm` under `security` must be set to +something other than SHA1 (see https://shattered.io/ ). Otherwise your +environment is not secure and will be exposed to attacks. + +In production also we highly recommended to register on the settings the IdP certificate instead of using the fingerprint method. The fingerprint, is a hash, so at the end is open to a collision attack that can end on a signature validation bypass. Other SAML toolkits deprecated that mechanism, we maintain it for compatibility and also to be used on test environment. + +Getting started +--------------- + +### Knowing the toolkit ### + +The new OneLogin SAML Toolkit contains different folders (`certs`, `endpoints`, +`lib`, `demo`, etc.) and some files. + +Let's start describing the folders: + +#### `certs/` #### + +SAML requires a x509 cert to sign and encrypt elements like `NameID`, `Message`, +`Assertion`, `Metadata`. + +If our environment requires sign or encrypt support, this folder may contain +the x509 cert and the private key that the SP will use: + + * `sp.crt` - The public cert of the SP + * `sp.key` - The private key of the SP + +Or also we can provide those data in the setting file at the `$settings['sp']['x509cert']` +and the `$settings['sp']['privateKey']`. + +Sometimes we could need a signature on the metadata published by the SP, in +this case we could use the x509 cert previously mentioned or use a new x.509 +cert: `metadata.crt` and `metadata.key`. + +Use `sp_new.crt` if you are in a key rollover process and you want to +publish that x509 certificate on Service Provider metadata. + +#### `src/` #### + +This folder contains the heart of the toolkit, the libraries: + + * `Saml2` folder contains the new version of the classes and methods that + are described in a later section. + + +#### `doc/` #### + +This folder contains the API documentation of the toolkit. + + +#### `endpoints/` #### + +The toolkit has three endpoints: + + * `metadata.php` - Where the metadata of the SP is published. + * `acs.php` - Assertion Consumer Service. Processes the SAML Responses. + * `sls.php` - Single Logout Service. Processes Logout Requests and Logout + Responses. + +You can use the files provided by the toolkit or create your own endpoints +files when adding SAML support to your applications. Take in mind that those +endpoints files uses the setting file of the toolkit's base folder. + + +#### `locale/` #### + +Locale folder contains some translations: `en_US` and `es_ES` as a proof of concept. +Currently there are no translations but we will eventually localize the messages +and support multiple languages. + + +#### Other important files #### + +* `settings_example.php` - A template to be used in order to create a + settings.php file which contains the basic configuration info of the toolkit. +* `advanced_settings_example.php` - A template to be used in order to create a + advanced_settings.php file which contains extra configuration info related to + the security, the contact person, and the organization associated to the SP. +* `_toolkit_loader.php` - This file load the toolkit libraries (The SAML2 lib). + + +#### Miscellaneous #### + +* `tests/` - Contains the unit test of the toolkit. +* `demo1/` - Contains an example of a simple PHP app with SAML support. + Read the `Readme.txt` inside for more info. +* `demo2/` - Contains another example. + + +### How it works ### + +#### Settings #### + +First of all we need to configure the toolkit. The SP's info, the IdP's info, +and in some cases, configure advanced security issues like signatures and +encryption. + +There are two ways to provide the settings information: + + * Use a `settings.php` file that we should locate at the base folder of the + toolkit. + * Use an array with the setting data and provide it directly to the + constructor of the class. + + +There is a template file, `settings_example.php`, so you can make a copy of this +file, rename and edit it. + +```php + true, + + // Enable debug mode (to print errors). + 'debug' => false, + + // Set a BaseURL to be used instead of try to guess + // the BaseURL of the view that process the SAML Message. + // Ex http://sp.example.com/ + // http://example.com/sp/ + 'baseurl' => null, + + // Service Provider Data that we are deploying. + 'sp' => array( + // Identifier of the SP entity (must be a URI) + 'entityId' => '', + // Specifies info about where and how the message MUST be + // returned to the requester, in this case our SP. + 'assertionConsumerService' => array( + // URL Location where the from the IdP will be returned + 'url' => '', + // SAML protocol binding to be used when returning the + // message. OneLogin Toolkit supports this endpoint for the + // HTTP-POST binding only. + 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + ), + // If you need to specify requested attributes, set a + // attributeConsumingService. nameFormat, attributeValue and + // friendlyName can be omitted + "attributeConsumingService"=> array( + "serviceName" => "SP test", + "serviceDescription" => "Test Service", + "requestedAttributes" => array( + array( + "name" => "", + "isRequired" => false, + "nameFormat" => "", + "friendlyName" => "", + "attributeValue" => array() + ) + ) + ), + // Specifies info about where and how the message MUST be + // returned to the requester, in this case our SP. + 'singleLogoutService' => array( + // URL Location where the from the IdP will be returned + 'url' => '', + // SAML protocol binding to be used when returning the + // message. OneLogin Toolkit supports the HTTP-Redirect binding + // only for this endpoint. + 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', + ), + // Specifies the constraints on the name identifier to be used to + // represent the requested subject. + // Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported. + 'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', + // Usually x509cert and privateKey of the SP are provided by files placed at + // the certs folder. But we can also provide them with the following parameters + 'x509cert' => '', + 'privateKey' => '', + + /* + * Key rollover + * If you plan to update the SP x509cert and privateKey + * you can define here the new x509cert and it will be + * published on the SP metadata so Identity Providers can + * read them and get ready for rollover. + */ + // 'x509certNew' => '', + ), + + // Identity Provider Data that we want connected with our SP. + 'idp' => array( + // Identifier of the IdP entity (must be a URI) + 'entityId' => '', + // SSO endpoint info of the IdP. (Authentication Request protocol) + 'singleSignOnService' => array( + // URL Target of the IdP where the Authentication Request Message + // will be sent. + 'url' => '', + // SAML protocol binding to be used when returning the + // message. OneLogin Toolkit supports the HTTP-Redirect binding + // only for this endpoint. + 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', + ), + // SLO endpoint info of the IdP. + 'singleLogoutService' => array( + // URL Location of the IdP where SLO Request will be sent. + 'url' => '', + // URL location of the IdP where SLO Response will be sent (ResponseLocation) + // if not set, url for the SLO Request will be used + 'responseUrl' => '', + // SAML protocol binding to be used when returning the + // message. OneLogin Toolkit supports the HTTP-Redirect binding + // only for this endpoint. + 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', + ), + // Public x509 certificate of the IdP + 'x509cert' => '', + /* + * Instead of use the whole x509cert you can use a fingerprint in order to + * validate a SAMLResponse, but we don't recommend to use that + * method on production since is exploitable by a collision attack. + * (openssl x509 -noout -fingerprint -in "idp.crt" to generate it, + * or add for example the -sha256 , -sha384 or -sha512 parameter) + * + * If a fingerprint is provided, then the certFingerprintAlgorithm is required in order to + * let the toolkit know which algorithm was used. Possible values: sha1, sha256, sha384 or sha512 + * 'sha1' is the default value. + * + * Notice that if you want to validate any SAML Message sent by the HTTP-Redirect binding, you + * will need to provide the whole x509cert. + */ + // 'certFingerprint' => '', + // 'certFingerprintAlgorithm' => 'sha1', + + /* In some scenarios the IdP uses different certificates for + * signing/encryption, or is under key rollover phase and + * more than one certificate is published on IdP metadata. + * In order to handle that the toolkit offers that parameter. + * (when used, 'x509cert' and 'certFingerprint' values are + * ignored). + */ + // 'x509certMulti' => array( + // 'signing' => array( + // 0 => '', + // ), + // 'encryption' => array( + // 0 => '', + // ) + // ), + ), +); +``` +In addition to the required settings data (IdP, SP), there is extra +information that could be defined. In the same way that a template exists +for the basic info, there is a template for that advanced info located +at the base folder of the toolkit and named `advanced_settings_example.php` +that you can copy and rename it as `advanced_settings.php` + +```php + array( + 'requests' => true, + 'responses' => true + ), + // Security settings + 'security' => array( + + /** signatures and encryptions offered */ + + // Indicates that the nameID of the sent by this SP + // will be encrypted. + 'nameIdEncrypted' => false, + + // Indicates whether the messages sent by this SP + // will be signed. [Metadata of the SP will offer this info] + 'authnRequestsSigned' => false, + + // Indicates whether the messages sent by this SP + // will be signed. + 'logoutRequestSigned' => false, + + // Indicates whether the messages sent by this SP + // will be signed. + 'logoutResponseSigned' => false, + + /* Sign the Metadata + False || True (use sp certs) || array ( + 'keyFileName' => 'metadata.key', + 'certFileName' => 'metadata.crt' + ) + || array ( + 'x509cert' => '', + 'privateKey' => '' + ) + */ + 'signMetadata' => false, + + /** signatures and encryptions required **/ + + // Indicates a requirement for the , + // and elements received by this SP to be signed. + 'wantMessagesSigned' => false, + + // Indicates a requirement for the elements received by + // this SP to be encrypted. + 'wantAssertionsEncrypted' => false, + + // Indicates a requirement for the elements received by + // this SP to be signed. [Metadata of the SP will offer this info] + 'wantAssertionsSigned' => false, + + // Indicates a requirement for the NameID element on the SAMLResponse + // received by this SP to be present. + 'wantNameId' => true, + + // Indicates a requirement for the NameID received by + // this SP to be encrypted. + 'wantNameIdEncrypted' => false, + + // Authentication context. + // Set to false and no AuthContext will be sent in the AuthNRequest. + // Set true or don't present this parameter and you will get an AuthContext 'exact' 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'. + // Set an array with the possible auth context values: array('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509'). + 'requestedAuthnContext' => true, + + // Indicates if the SP will validate all received xmls. + // (In order to validate the xml, 'strict' and 'wantXMLValidation' must be true). + 'wantXMLValidation' => true, + + // If true, SAMLResponses with an empty value at its Destination + // attribute will not be rejected for this fact. + 'relaxDestinationValidation' => false, + + // Algorithm that the toolkit will use on signing process. Options: + // 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' + // 'http://www.w3.org/2000/09/xmldsig#dsa-sha1' + // 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' + // 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384' + // 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' + // Notice that rsa-sha1 is a deprecated algorithm and should not be used + 'signatureAlgorithm' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256', + + // Algorithm that the toolkit will use on digest process. Options: + // 'http://www.w3.org/2000/09/xmldsig#sha1' + // 'http://www.w3.org/2001/04/xmlenc#sha256' + // 'http://www.w3.org/2001/04/xmldsig-more#sha384' + // 'http://www.w3.org/2001/04/xmlenc#sha512' + // Notice that sha1 is a deprecated algorithm and should not be used + 'digestAlgorithm' => 'http://www.w3.org/2001/04/xmlenc#sha256', + + // ADFS URL-Encodes SAML data as lowercase, and the toolkit by default uses + // uppercase. Turn it True for ADFS compatibility on signature verification + 'lowercaseUrlencoding' => false, + ), + + // Contact information template, it is recommended to supply a + // technical and support contacts. + 'contactPerson' => array( + 'technical' => array( + 'givenName' => '', + 'emailAddress' => '' + ), + 'support' => array( + 'givenName' => '', + 'emailAddress' => '' + ), + ), + + // Organization information template, the info in en_US lang is + // recomended, add more if required. + 'organization' => array( + 'en-US' => array( + 'name' => '', + 'displayname' => '', + 'url' => '' + ), + ), +); +``` + +The compression settings allow you to instruct whether or not the IdP can accept +data that has been compressed using [gzip](gzip) ('requests' and 'responses'). +But if we provide a `$deflate` boolean parameter to the `getRequest` or `getResponse` method it will have priority over the compression settings. + +In the security section, you can set the way that the SP will handle the messages +and assertions. Contact the admin of the IdP and ask him what the IdP expects, +and decide what validations will handle the SP and what requirements the SP will have +and communicate them to the IdP's admin too. + +Once we know what kind of data could be configured, let's talk about the way +settings are handled within the toolkit. + +The settings files described (`settings.php` and `advanced_settings.php`) are loaded +by the toolkit if no other array with settings info is provided in the constructor of the toolkit. Let's see some examples. + +```php +// Initializes toolkit with settings.php & advanced_settings files. +$auth = new OneLogin\Saml2\Auth(); +//or +$settings = new OneLogin\Saml2\Settings(); + +// Initializes toolkit with the array provided. +$auth = new OneLogin\Saml2\Auth($settingsInfo); +//or +$settings = new OneLogin\Saml2\Settings($settingsInfo); +``` + +You can declare the `$settingsInfo` in the file that contains the constructor +execution or locate them in any file and load the file in order to get the +array available as we see in the following example: + +```php +login(); // Method that sent the AuthNRequest +``` + +The `AuthNRequest` will be sent signed or unsigned based on the security info +of the `advanced_settings.php` (`'authnRequestsSigned'`). + + +The IdP will then return the SAML Response to the user's client. The client is then forwarded to the Attribute Consumer Service of the SP with this information. If we do not set a `'url'` param in the login method and we are using the default ACS provided by the toolkit (`endpoints/acs.php`), then the ACS endpoint will redirect the user to the file that launched the SSO request. + +We can set a `'returnTo'` url to change the workflow and redirect the user to the other PHP file. + +```php +$newTargetUrl = 'http://example.com/consume2.php'; +$auth = new OneLogin\Saml2\Auth(); +$auth->login($newTargetUrl); +``` + +The login method can receive other six optional parameters: + +* `$parameters` - An array of parameters that will be added to the `GET` in the HTTP-Redirect. +* `$forceAuthn` - When true the `AuthNRequest` will set the `ForceAuthn='true'` +* `$isPassive` - When true the `AuthNRequest` will set the `Ispassive='true'` +* `$strict` - True if we want to stay (returns the url string) False to redirect +* `$setNameIdPolicy` - When true the AuthNRequest will set a nameIdPolicy element. +* `$nameIdValueReq` - Indicates to the IdP the subject that should be authenticated. + +If a match on the future SAMLResponse ID and the AuthNRequest ID to be sent is required, that AuthNRequest ID must to be extracted and saved. + +```php +$ssoBuiltUrl = $auth->login(null, array(), false, false, true); +$_SESSION['AuthNRequestID'] = $auth->getLastRequestID(); +header('Pragma: no-cache'); +header('Cache-Control: no-cache, must-revalidate'); +header('Location: ' . $ssoBuiltUrl); +exit(); +``` + +#### The SP Endpoints #### + +Related to the SP there are three important views: The metadata view, the ACS view and the SLS view. The toolkit +provides examples of those views in the endpoints directory. + +##### SP Metadata `endpoints/metadata.php` ##### + +This code will provide the XML metadata file of our SP, based on the info that we provided in the settings files. + +```php +getSettings(); + $metadata = $settings->getSPMetadata(); + $errors = $settings->validateMetadata($metadata); + if (empty($errors)) { + header('Content-Type: text/xml'); + echo $metadata; + } else { + throw new OneLogin\Saml2\Error( + 'Invalid SP metadata: '.implode(', ', $errors), + OneLogin\Saml2\Error::METADATA_SP_INVALID + ); + } +} catch (Exception $e) { + echo $e->getMessage(); +} +``` +The `getSPMetadata` will return the metadata signed or not based +on the security info of the `advanced_settings.php` (`'signMetadata'`). + +Before the XML metadata is exposed, a check takes place to ensure +that the info to be provided is valid. + +Instead of use the Auth object, you can directly use + +```php +$settings = new OneLogin\Saml2\Settings($settingsInfo, true); +``` +to get the settings object and with the true parameter we will avoid the IdP Settings validation. + + +##### Attribute Consumer Service(ACS) `endpoints/acs.php` ##### + +This code handles the SAML response that the IdP forwards to the SP through the user's client. + +```php +processResponse($requestID); +unset($_SESSION['AuthNRequestID']); + +$errors = $auth->getErrors(); + +if (!empty($errors)) { + echo '

' . implode(', ', $errors) . '

'; + exit(); +} + +if (!$auth->isAuthenticated()) { + echo "

Not authenticated

"; + exit(); +} + +$_SESSION['samlUserdata'] = $auth->getAttributes(); +$_SESSION['samlNameId'] = $auth->getNameId(); +$_SESSION['samlNameIdFormat'] = $auth->getNameIdFormat(); +$_SESSION['samlNameidNameQualifier'] = $auth->getNameIdNameQualifier(); +$_SESSION['samlNameidSPNameQualifier'] = $auth->getNameIdSPNameQualifier(); +$_SESSION['samlSessionIndex'] = $auth->getSessionIndex(); + +if (isset($_POST['RelayState']) && OneLogin\Saml2\Utils::getSelfURL() != $_POST['RelayState']) { + $auth->redirectTo($_POST['RelayState']); +} + +$attributes = $_SESSION['samlUserdata']; +$nameId = $_SESSION['samlNameId']; + +echo '

Identified user: '. htmlentities($nameId) .'

'; + +if (!empty($attributes)) { + echo '

' . _('User attributes:') . '

'; + echo ''; + foreach ($attributes as $attributeName => $attributeValues) { + echo ''; + } + echo '
' . _('Name') . '' . _('Values') . '
' . htmlentities($attributeName) . '
    '; + foreach ($attributeValues as $attributeValue) { + echo '
  • ' . htmlentities($attributeValue) . '
  • '; + } + echo '
'; +} else { + echo _('No attributes found.'); +} +``` + +The SAML response is processed and then checked that there are no errors. +It also verifies that the user is authenticated and stored the userdata in session. + +At that point there are two possible alternatives: + + 1. If no `RelayState` is provided, we could show the user data in this view + or however we wanted. + + 2. If `RelayState` is provided, a redirection takes place. + +Notice that we saved the user data in the session before the redirection to +have the user data available at the `RelayState` view. + + +###### The `getAttributes` method ###### + +In order to retrieve attributes we can use: + +```php +$attributes = $auth->getAttributes(); +``` + +With this method we get all the user data provided by the IdP in the Assertion +of the SAML Response. + +If we execute ```print_r($attributes)``` we could get: + +```php +Array +( + [cn] => Array + ( + [0] => John + ) + [sn] => Array + ( + [0] => Doe + ) + [mail] => Array + ( + [0] => john.doe@example.com + ) + [groups] => Array + ( + [0] => users + [1] => members + ) +) +``` + +Each attribute name can be used as an index into `$attributes` to obtain the value. Every attribute value +is an array - a single-valued attribute is an array of a single element. + + +The following code is equivalent: + +```php +$attributes = $auth->getAttributes(); +print_r($attributes['cn']); +``` + +```php +print_r($auth->getAttribute('cn')); +``` + + +Before trying to get an attribute, check that the user is +authenticated. If the user isn't authenticated or if there were +no attributes in the SAML assertion, an empty array will be +returned. For example, if we call to `getAttributes` before a +`$auth->processResponse`, the `getAttributes()` will return an +empty array. + + +##### Single Logout Service (SLS) `endpoints/sls.php` ##### + +This code handles the Logout Request and the Logout Responses. + +```php +processSLO(false, $requestID); + +$errors = $auth->getErrors(); + +if (empty($errors)) { + echo 'Sucessfully logged out'; +} else { + echo implode(', ', $errors); +} +``` + +If the SLS endpoints receives a Logout Response, the response is +validated and the session could be closed + + + +```php +// part of the processSLO method + +$logoutResponse = new OneLogin\Saml2\LogoutResponse($this->_settings, $_GET['SAMLResponse']); +if (!$logoutResponse->isValid($requestId)) { + $this->_errors[] = 'invalid_logout_response'; +} else if ($logoutResponse->getStatus() !== OneLogin\Saml2\Constants::STATUS_SUCCESS) { + $this->_errors[] = 'logout_not_success'; +} else { + if (!$keepLocalSession) { + OneLogin\Saml2\Utils::deleteLocalSession(); + } +} +``` + +If the SLS endpoints receives an Logout Request, the request is validated, +the session is closed and a Logout Response is sent to the SLS endpoint of +the IdP. + +```php +// part of the processSLO method + +$decoded = base64_decode($_GET['SAMLRequest']); +$request = gzinflate($decoded); +if (!OneLogin\Saml2\LogoutRequest::isValid($this->_settings, $request)) { + $this->_errors[] = 'invalid_logout_request'; +} else { + if (!$keepLocalSession) { + OneLogin\Saml2\Utils::deleteLocalSession(); + } + + $inResponseTo = $request->id; + $responseBuilder = new OneLogin\Saml2\LogoutResponse($this->_settings); + $responseBuilder->build($inResponseTo); + $logoutResponse = $responseBuilder->getResponse(); + + $parameters = array('SAMLResponse' => $logoutResponse); + if (isset($_GET['RelayState'])) { + $parameters['RelayState'] = $_GET['RelayState']; + } + + $security = $this->_settings->getSecurityData(); + if (isset($security['logoutResponseSigned']) && $security['logoutResponseSigned']) { + $signature = $this->buildResponseSignature($logoutResponse, $parameters['RelayState'], $security['signatureAlgorithm']); + $parameters['SigAlg'] = $security['signatureAlgorithm']; + $parameters['Signature'] = $signature; + } + + $this->redirectTo($this->getSLOurl(), $parameters); +} +``` + +If you aren't using the default PHP session, or otherwise need a manual +way to destroy the session, you can pass a callback method to the +`processSLO` method as the fourth parameter + +```php +$keepLocalSession = False; +$callback = function () { + // Destroy user session +}; + +$auth->processSLO($keepLocalSession, null, false, $callback); +``` + + +If we don't want that `processSLO` to destroy the session, pass a true +parameter to the `processSLO` method + +```php +$keepLocalSession = True; +$auth->processSLO($keepLocalSession); +``` + +#### Initiate SLO #### + +In order to send a Logout Request to the IdP: + +```php +logout(); // Method that sent the Logout Request. +``` + +Also there are eight optional parameters that can be set: +* `$returnTo` - The target URL the user should be returned to after logout. +* `$parameters` - Extra parameters to be added to the GET. +* `$name_id` - That will be used to build the LogoutRequest. If `name_id` parameter is not set and the auth object processed a +SAML Response with a `NameId`, then this `NameId` will be used. +* `$session_index` - SessionIndex that identifies the session of the user. +* `$stay` - True if we want to stay (returns the url string) False to redirect. +* `$nameIdFormat` - The NameID Format will be set in the LogoutRequest. +* `$nameIdNameQualifier` - The NameID NameQualifier will be set in the LogoutRequest. +* `$nameIdSPNameQualifier` - The NameID SP NameQualifier will be set in the LogoutRequest. + +The Logout Request will be sent signed or unsigned based on the security +info of the `advanced_settings.php` (`'logoutRequestSigned'`). + +The IdP will return the Logout Response through the user's client to the +Single Logout Service of the SP. +If we do not set a `'url'` param in the logout method and are using the +default SLS provided by the toolkit (`endpoints/sls.php`), then the SLS +endpoint will redirect the user to the file that launched the SLO request. + +We can set an `'returnTo'` url to change the workflow and redirect the user +to other php file. + +```php +$newTargetUrl = 'http://example.com/loggedOut.php'; +$auth = new OneLogin\Saml2\Auth(); +$auth->logout($newTargetUrl); +``` +A more complex logout with all the parameters: +``` +$auth = new OneLogin\Saml2\Auth(); +$returnTo = null; +$parameters = array(); +$nameId = null; +$sessionIndex = null; +$nameIdFormat = null; +$nameIdNameQualifier = null; +$nameIdSPNameQualifier = null; + +if (isset($_SESSION['samlNameId'])) { + $nameId = $_SESSION['samlNameId']; +} +if (isset($_SESSION['samlSessionIndex'])) { + $sessionIndex = $_SESSION['samlSessionIndex']; +} +if (isset($_SESSION['samlNameIdFormat'])) { + $nameIdFormat = $_SESSION['samlNameIdFormat']; +} +if (isset($_SESSION['samlNameIdNameQualifier'])) { + $nameIdNameQualifier = $_SESSION['samlNameIdNameQualifier']; +} +if (isset($_SESSION['samlNameIdSPNameQualifier'])) { + $nameIdSPNameQualifier = $_SESSION['samlNameIdSPNameQualifier']; +} +$auth->logout($returnTo, $parameters, $nameId, $sessionIndex, false, $nameIdFormat, $nameIdNameQualifier, $nameIdSPNameQualifier); +``` + +If a match on the future LogoutResponse ID and the LogoutRequest ID to be sent is required, that LogoutRequest ID must to be extracted and stored. + +```php +$sloBuiltUrl = $auth->logout(null, $parameters, $nameId, $sessionIndex, true); +$_SESSION['LogoutRequestID'] = $auth->getLastRequestID(); +header('Pragma: no-cache'); +header('Cache-Control: no-cache, must-revalidate'); +header('Location: ' . $sloBuiltUrl); +exit(); +``` + +#### Example of a view that initiates the SSO request and handles the response (is the acs target) #### + +We can code a unique file that initiates the SSO process, handle the response, get the attributes, initiate +the SLO and processes the logout response. + +Note: Review the `demo1` folder that contains that use case; in a later section we +explain the demo1 use case further in detail. + +```php +login(); +} else if (isset($_GET['sso2'])) { // Another SSO action + $returnTo = $spBaseUrl.'/demo1/attrs.php'; // but set a custom RelayState URL + $auth->login($returnTo); +} else if (isset($_GET['slo'])) { // SLO action. Will sent a Logout Request to IdP + $auth->logout(); +} else if (isset($_GET['acs'])) { // Assertion Consumer Service + $auth->processResponse(); // Process the Response of the IdP, get the + // attributes and put then at + // $_SESSION['samlUserdata'] + + $errors = $auth->getErrors(); // This method receives an array with the errors + // that could took place during the process + + if (!empty($errors)) { + echo '

' . implode(', ', $errors) . '

'; + } + // This check if the response was + if (!$auth->isAuthenticated()) { // sucessfully validated and the user + echo '

Not authenticated

'; // data retrieved or not + exit(); + } + + $_SESSION['samlUserdata'] = $auth->getAttributes(); // Retrieves user data + if (isset($_POST['RelayState']) && OneLogin\Saml2\Utils::getSelfURL() != $_POST['RelayState']) { + $auth->redirectTo($_POST['RelayState']); // Redirect if there is a + } // relayState set +} else if (isset($_GET['sls'])) { // Single Logout Service + $auth->processSLO(); // Process the Logout Request & Logout Response + $errors = $auth->getErrors(); // Retrieves possible validation errors + if (empty($errors)) { + echo '

Sucessfully logged out

'; + } else { + echo '

' . implode(', ', $errors) . '

'; + } +} + +if (isset($_SESSION['samlUserdata'])) { // If there is user data we print it. + if (!empty($_SESSION['samlUserdata'])) { + $attributes = $_SESSION['samlUserdata']; + echo 'You have the following attributes:
'; + echo ''; + foreach ($attributes as $attributeName => $attributeValues) { + echo ''; + } + echo '
NameValues
' . htmlentities($attributeName) . '
    '; + foreach ($attributeValues as $attributeValue) { + echo '
  • ' . htmlentities($attributeValue) . '
  • '; + } + echo '
'; + } else { // If there is not user data, we notify + echo "

You don't have any attribute

"; + } + + echo '

Logout

'; // Print some links with possible +} else { // actions + echo '

Login

'; + echo '

Login and access to attrs.php page

'; +} +``` + +#### URL-guessing methods #### + +php-saml toolkit uses a bunch of methods in OneLogin\Saml2\Utils that try to guess the URL where the SAML messages are processed. + +* `getSelfHost` Returns the current host. +* `getSelfPort` Return the port number used for the request +* `isHTTPS` Checks if the protocol is https or http. +* `getSelfURLhost` Returns the protocol + the current host + the port (if different than common ports). +* `getSelfURL` Returns the URL of the current host + current view + query. +* `getSelfURLNoQuery` Returns the URL of the current host + current view. +* `getSelfRoutedURLNoQuery` Returns the routed URL of the current host + current view. + +getSelfURLNoQuery and getSelfRoutedURLNoQuery are used to calculate the currentURL in order to validate SAML elements like Destination or Recipient. + +When the PHP application is behind a proxy or a load balancer we can execute `setProxyVars(true)` and `setSelfPort` and `isHTTPS` will take care of the `$_SERVER["HTTP_X_FORWARDED_PORT"]` and `$_SERVER['HTTP_X_FORWARDED_PROTO']` vars (otherwise they are ignored). + +Also a developer can use `setSelfProtocol`, `setSelfHost`, `setSelfPort` and `getBaseURLPath` to define a specific value to be returned by `isHTTPS`, `getSelfHost`, `getSelfPort` and `getBaseURLPath`. And define a `setBasePath` to be used on the `getSelfURL` and `getSelfRoutedURLNoQuery` to replace the data extracted from `$_SERVER["REQUEST_URI"]`. + +At the settings the developer will be able to set a `'baseurl'` parameter that automatically will use `setBaseURL` to set values for `setSelfProtocol`, `setSelfHost`, `setSelfPort` and `setBaseURLPath`. + + +### Working behind load balancer ### + +Is possible that asserting request URL and Destination attribute of SAML response fails when working behind load balancer with SSL offload. + +You should be able to workaround this by configuring your server so that it is aware of the proxy and returns the original url when requested. + +Or by using the method described on the previous section. + + +### SP Key rollover ### + +If you plan to update the SP x509cert and privateKey you can define the new x509cert as `$settings['sp']['x509certNew']` and it will be +published on the SP metadata so Identity Providers can read them and get ready for rollover. + + +### IdP with multiple certificates ### + +In some scenarios the IdP uses different certificates for +signing/encryption, or is under key rollover phase and more than one certificate is published on IdP metadata. + +In order to handle that the toolkit offers the `$settings['idp']['x509certMulti']` parameter. + +When that parameter is used, `'x509cert'` and `'certFingerprint'` values will be ignored by the toolkit. + +The `x509certMulti` is an array with 2 keys: +- `signing`. An array of certs that will be used to validate IdP signature +- `encryption` An array with one unique cert that will be used to encrypt data to be sent to the IdP + + +### Replay attacks ### + +In order to avoid replay attacks, you can store the ID of the SAML messages already processed, to avoid processing them twice. Since the Messages expires and will be invalidated due that fact, you don't need to store those IDs longer than the time frame that you currently accepting. + +Get the ID of the last processed message/assertion with the `getLastMessageId`/`getLastAssertionId` methods of the Auth object. + + +### Main classes and methods ### + +Described below are the main classes and methods that can be invoked. + +#### Saml2 library #### + +Lets describe now the classes and methods of the SAML2 library. + +##### OneLogin\Saml2\Auth - Auth.php ##### + +Main class of OneLogin PHP Toolkit + + * `Auth` - Initializes the SP SAML instance + * `login` - Initiates the SSO process. + * `logout` - Initiates the SLO process. + * `processResponse` - Process the SAML Response sent by the IdP. + * `processSLO` - Process the SAML Logout Response / Logout Request sent by the + IdP. + * `redirectTo` - Redirects the user to the url past by parameter or to the url + that we defined in our SSO Request. + * `isAuthenticated` - Checks if the user is authenticated or not. + * `getAttributes` - Returns the set of SAML attributes. + * `getAttribute` - Returns the requested SAML attribute + * `getNameId` - Returns the nameID + * `getNameIdFormat` - Gets the NameID Format provided by the SAML response from the IdP. + * `getNameIdNameQualifier` - Gets the NameID NameQualifier provided from the SAML Response String. + * `getNameIdNameSPQualifier` - Gets the NameID SP NameQualifier provided from the SAML Response String. + * `getSessionIndex` - Gets the SessionIndex from the AuthnStatement. + * `getErrors` - Returns if there were any error + * `getSSOurl` - Gets the SSO url. + * `getSLOurl` - Gets the SLO url. + * `getLastRequestID` - The ID of the last Request SAML message generated. + * `buildRequestSignature` - Generates the Signature for a SAML Request + * `buildResponseSignature` - Generates the Signature for a SAML Response + * `getSettings` - Returns the settings info + * `setStrict` - Set the strict mode active/disable + * `getLastRequestID` - Gets the ID of the last AuthNRequest or LogoutRequest generated by the Service Provider. + * `getLastRequestXML` - Returns the most recently-constructed/processed XML SAML request (AuthNRequest, LogoutRequest) + * `getLastResponseXML` - Returns the most recently-constructed/processed XML SAML response (SAMLResponse, LogoutResponse). If the SAMLResponse had an encrypted assertion, decrypts it. + + +##### OneLogin\Saml2\AuthnRequest - `AuthnRequest.php` ##### + +SAML 2 Authentication Request class + + * `AuthnRequest` - Constructs the `AuthnRequest` object. + * `getRequest` - Returns deflated, base64 encoded, unsigned `AuthnRequest`. + * `getId` - Returns the `AuthNRequest` ID. + * `getXML` - Returns the XML that will be sent as part of the request. + +##### OneLogin\Saml2\Response - `Response.php` ##### + +SAML 2 Authentication Response class + + * `Response` - Constructs the SAML Response object. + * `isValid` - Determines if the SAML Response is valid using the certificate. + * `checkStatus` - Checks if the Status is success. + * `getAudiences` - Gets the audiences. + * `getIssuers` - Gets the Issuers (from Response and Assertion) + * `getNameIdData` - Gets the NameID Data provided by the SAML response from the + IdP. + * `getNameId` - Gets the NameID provided by the SAML response from the IdP. + * `getNameIdFormat` - Gets the NameID Format provided by the SAML response from the IdP. + * `getNameIdNameQualifier` - Gets the NameID NameQualifier provided from the SAML Response String. + * `getNameIdNameSPQualifier` - Gets the NameID SP NameQualifier provided from the SAML Response String. + * `getSessionNotOnOrAfter` - Gets the SessionNotOnOrAfter from the + AuthnStatement + * `getSessionIndex` - Gets the SessionIndex from the AuthnStatement. + * `getAttributes` - Gets the Attributes from the AttributeStatement element. + * `validateNumAssertions` - Verifies that the document only contains a single + Assertion (encrypted or not). + * `validateTimestamps` - Verifies that the document is still valid according + Conditions Element. + * `getError` - After executing a validation process, if it fails, this method returns the cause + * `getXMLDocument` - Returns the SAML Response document (If contains an encrypted assertion, decrypts it) + +##### OneLogin\Saml2\LogoutRequest - `LogoutRequest.php` ##### + +SAML 2 Logout Request class + + * `LogoutRequest` - Constructs the Logout Request object. + * `getRequest` - Returns the Logout Request defated, base64encoded, unsigned + * `getID` - Returns the ID of the Logout Request. (If you have the object you can access to the id attribute) + * `getNameIdData` - Gets the NameID Data of the the Logout Request. + * `getNameId` - Gets the NameID of the Logout Request. + * `getIssuer` - Gets the Issuer of the Logout Request. + * `getSessionIndexes` - Gets the SessionIndexes from the Logout Request. + * `isValid` - Checks if the Logout Request received is valid. + * `getError` - After executing a validation process, if it fails, this method returns the cause + * `getXML` - Returns the XML that will be sent as part of the request or that was received at the SP. + +##### OneLogin\Saml2\LogoutResponse - `LogoutResponse.php` ##### + +SAML 2 Logout Response class + + * `LogoutResponse` - Constructs a Logout Response object + (Initialize params from settings and if provided load the Logout Response) + * `getIssuer` - Gets the Issuer of the Logout Response. + * `getStatus` - Gets the Status of the Logout Response. + * `isValid` - Determines if the SAML LogoutResponse is valid + * `build` - Generates a Logout Response object. + * `getResponse` - Returns a Logout Response object. + * `getError` - After executing a validation process, if it fails, this method returns the cause. + * `getXML` - Returns the XML that will be sent as part of the response or that was received at the SP. + +##### OneLogin\Saml2\Settings - `Settings.php` ##### + +Configuration of the OneLogin PHP Toolkit + + * `Settings` - Initializes the settings: Sets the paths of + the different folders and Loads settings info from settings file or + array/object provided + * `checkSettings` - Checks the settings info. + * `getBasePath` - Returns base path. + * `getCertPath` - Returns cert path. + * `getLibPath` - Returns lib path. + * `getExtLibPath` - Returns external lib path. + * `getSchemasPath` - Returns schema path. + * `checkSPCerts` - Checks if the x509 certs of the SP exists and are valid. + * `getSPkey` - Returns the x509 private key of the SP. + * `getSPcert` - Returns the x509 public cert of the SP. + * `getSPcertNew` - Returns the future x509 public cert of the SP. + * `getIdPData` - Gets the IdP data. + * `getSPData`Gets the SP data. + * `getSecurityData` - Gets security data. + * `getContacts` - Gets contact data. + * `getOrganization` - Gets organization data. + * `getSPMetadata` - Gets the SP metadata. The XML representation. + * `validateMetadata` - Validates an XML SP Metadata. + * `formatIdPCert` - Formats the IdP cert. + * `formatSPCert` - Formats the SP cert. + * `formatSPCertNew` - Formats the SP cert new. + * `formatSPKey` - Formats the SP private key. + * `getErrors` - Returns an array with the errors, the array is empty when + the settings is ok. + * `getLastErrorReason` - Returns the reason of the last error + * `getBaseURL` - Returns the baseurl set on the settings if any. + * `setBaseURL` - Set a baseurl value + * `setStrict` - Activates or deactivates the strict mode. + * `isStrict` - Returns if the 'strict' mode is active. + * `isDebugActive` - Returns if the debug is active. + +##### OneLogin\Saml2\Metadata - `Metadata.php` ##### + +A class that contains functionality related to the metadata of the SP + +* `builder` - Generates the metadata of the SP based on the settings. +* `signmetadata` - Signs the metadata with the key/cert provided +* `addX509KeyDescriptors` - Adds the x509 descriptors (sign/encriptation) to + the metadata + +##### OneLogin\Saml2\Utils - `Utils.php` ##### + +Auxiliary class that contains several methods + + * `validateXML` - This function attempts to validate an XML string against + the specified schema. + * `formatCert` - Returns a x509 cert (adding header & footer if required). + * `formatPrivateKey` - returns a RSA private key (adding header & footer if required). + * `redirect` - Executes a redirection to the provided url (or return the + target url). + * `isHTTPS` - Checks if https or http. + * `getSelfHost` - Returns the current host. + * `getSelfURLhost` - Returns the protocol + the current host + the port + (if different than common ports). + * `getSelfURLNoQuery` - Returns the URL of the current host + current view. + * `getSelfURL` - Returns the URL of the current host + current view + query. + * `generateUniqueID` - Generates a unique string (used for example as ID + for assertions). + * `parseTime2SAML` - Converts a UNIX timestamp to SAML2 timestamp on the + form `yyyy-mm-ddThh:mm:ss(\.s+)?Z`. + * `parseSAML2Time` - Converts a SAML2 timestamp on the form + `yyyy-mm-ddThh:mm:ss(\.s+)?Z` to a UNIX timestamp. The sub-second part is + ignored. + * `parseDuration` - Interprets a ISO8601 duration value relative to a given + timestamp. + * `getExpireTime` - Compares two dates and returns the earliest. + * `query` - Extracts nodes from the DOMDocument. + * `isSessionStarted` - Checks if the session is started or not. + * `deleteLocalSession` - Deletes the local session. + * `calculateX509Fingerprint` - Calculates the fingerprint of a x509cert. + * `formatFingerPrint` - Formats a fingerprint. + * `generateNameId` - Generates a `nameID`. + * `getStatus` - Gets Status from a Response. + * `decryptElement` - Decrypts an encrypted element. + * `castKey` - Converts a `XMLSecurityKey` to the correct algorithm. + * `addSign` - Adds signature key and senders certificate to an element + (Message or Assertion). + * `validateSign` - Validates a signature (Message or Assertion). + +##### OneLogin\Saml2\IdPMetadataParser - `IdPMetadataParser.php` ##### + +Auxiliary class that contains several methods to retrieve and process IdP metadata + + * `parseRemoteXML` - Get IdP Metadata Info from URL. + * `parseFileXML` - Get IdP Metadata Info from File. + * `parseXML` - Get IdP Metadata Info from XML. + * `injectIntoSettings` - Inject metadata info into php-saml settings array. + + +For more info, look at the source code; each method is documented and details +about what it does and how to use it are provided. Make sure to also check the doc folder where +HTML documentation about the classes and methods is provided for SAML and +SAML2. + + +Demos included in the toolkit +----------------------------- + +The toolkit includes three demo apps to teach how use the toolkit, take a look on it. + +Demos require that SP and IdP are well configured before test it. + +## Demo1 ## + +### SP setup ### + +The Onelogin's PHP Toolkit allows you to provide the settings info in two ways: + + * Use a `settings.php` file that we should locate at the base folder of the + toolkit. + * Use an array with the setting data. + +In this demo we provide the data in the second way, using a setting array named +`$settingsInfo`. This array users the `settings_example.php` included as a template +to create the `settings.php` settings and store it in the `demo1/` folder. +Configure the SP part and later review the metadata of the IdP and complete the IdP info. + +If you check the code of the index.php file you will see that the `settings.php` +file is loaded in order to get the `$settingsInfo` var to be used in order to initialize +the `Setting` class. + +Notice that in this demo, the `setting.php` file that could be defined at the base +folder of the toolkit is ignored and the libs are loaded using the +`_toolkit_loader.php` located at the base folder of the toolkit. + + +### IdP setup ### + +Once the SP is configured, the metadata of the SP is published at the +`metadata.php` file. Configure the IdP based on that information. + + +### How it works ### + + 1. First time you access to `index.php` view, you can select to login and return + to the same view or login and be redirected to the `attrs.php` view. + + 2. When you click: + + 2.1 in the first link, we access to (`index.php?sso`) an `AuthNRequest` + is sent to the IdP, we authenticate at the IdP and then a Response is sent + through the user's client to the SP, specifically the Assertion Consumer Service view: `index.php?acs`. + Notice that a `RelayState` parameter is set to the url that initiated the + process, the `index.php` view. + + 2.2 in the second link we access to (`attrs.php`) have the same process + described at 2.1 with the difference that as `RelayState` is set the `attrs.php`. + + 3. The SAML Response is processed in the ACS (`index.php?acs`), if the Response + is not valid, the process stops here and a message is shown. Otherwise we + are redirected to the RelayState view. a) `index.php` or b) `attrs.php`. + + 4. We are logged in the app and the user attributes are showed. + At this point, we can test the single log out functionality. + + 5. The single log out functionality could be tested by two ways. + + 5.1 SLO Initiated by SP. Click on the "logout" link at the SP, after that a + Logout Request is sent to the IdP, the session at the IdP is closed and + replies through the client to the SP with a Logout Response (sent to the + Single Logout Service endpoint). The SLS endpoint (`index.php?sls`) of the SP + process the Logout Response and if is valid, close the user session of the + local app. Notice that the SLO Workflow starts and ends at the SP. + + 5.2 SLO Initiated by IdP. In this case, the action takes place on the IdP + side, the logout process is initiated at the idP, sends a Logout + Request to the SP (SLS endpoint, `index.php?sls`). The SLS endpoint of the SP + process the Logout Request and if is valid, close the session of the user + at the local app and send a Logout Response to the IdP (to the SLS endpoint + of the IdP). The IdP receives the Logout Response, process it and close the + session at of the IdP. Notice that the SLO Workflow starts and ends at the IdP. + +Notice that all the SAML Requests and Responses are handled by a unique file, +the `index.php` file and how `GET` parameters are used to know the action that +must be done. + + +## Demo2 ## + +### SP setup ### + +The Onelogin's PHP Toolkit allows you to provide the settings info in two ways: + + * Use a `settings.php` file that we should locate at the base folder of the + toolkit. + * Use an array with the setting data. + +The first is the case of the demo2 app. The `setting.php` file and the +`setting_extended.php` file should be defined at the base folder of the toolkit. +Review the `setting_example.php` and the `advanced_settings_example.php` to +learn how to build them. + +In this case as Attribute Consume Service and Single Logout Service we are going to +use the files located in the endpoint folder (`acs.php` and `sls.php`). + + +### IdP setup ### + +Once the SP is configured, the metadata of the SP is published at the +`metadata.php` file. Based on that info, configure the IdP. + + +### How it works ### + +At demo1, we saw how all the SAML Request and Responses were handler at an +unique file, the `index.php` file. This demo1 uses high-level programming. + +At demo2, we have several views: `index.php`, `sso.php`, `slo.php`, `consume.php` +and `metadata.php`. As we said, we will use the endpoints that are defined +in the toolkit (`acs.php`, `sls.php` of the endpoints folder). This demo2 uses +low-level programming. + +Notice that the SSO action can be initiated at `index.php` or `sso.php`. + +The SAML workflow that take place is similar that the workflow defined in the +demo1, only changes the targets. + + 1. When you access `index.php` or `sso.php` for the first time, an `AuthNRequest` is + sent to the IdP automatically, (as `RelayState` is sent the origin url). + We authenticate at the IdP and then a `Response` is sent to the SP, to the + ACS endpoint, in this case `acs.php` of the endpoints folder. + + 2. The SAML Response is processed in the ACS, if the `Response` is not valid, + the process stops here and a message is shown. Otherwise we are redirected + to the `RelayState` view (`sso.php` or `index.php`). The `sso.php` detects if the + user is logged and redirects to `index.php`, so we will be in the + `index.php` at the end. + + 3. We are logged into the app and the user attributes (if any) are shown. + At this point, we can test the single log out functionality. + + 4. The single log out functionality could be tested by two ways. + + 4.1 SLO Initiated by SP. Click on the "logout" link at the SP, after that + we are redirected to the `slo.php` view and there a Logout Request is sent + to the IdP, the session at the IdP is closed and replies to the SP a + Logout Response (sent to the Single Logout Service endpoint). In this case + The SLS endpoint of the SP process the Logout Response and if is + valid, close the user session of the local app. Notice that the SLO + Workflow starts and ends at the SP. + + 4.2 SLO Initiated by IdP. In this case, the action takes place on the IdP + side, the logout process is initiated at the idP, sends a Logout + Request to the SP (SLS endpoint `sls.php` of the endpoint folder). + The SLS endpoint of the SP process the Logout Request and if is valid, + close the session of the user at the local app and sends a Logout Response + to the IdP (to the SLS endpoint of the IdP).The IdP receives the Logout + Response, process it and close the session at of the IdP. Notice that the + SLO Workflow starts and ends at the IdP. + diff --git a/vendor/onelogin/php-saml/_toolkit_loader.php b/vendor/onelogin/php-saml/_toolkit_loader.php new file mode 100644 index 0000000000..c4649d7633 --- /dev/null +++ b/vendor/onelogin/php-saml/_toolkit_loader.php @@ -0,0 +1,34 @@ + array( + 'requests' => true, + 'responses' => true + ), + + // Security settings + 'security' => array( + + /** signatures and encryptions offered */ + + // Indicates that the nameID of the sent by this SP + // will be encrypted. + 'nameIdEncrypted' => false, + + // Indicates whether the messages sent by this SP + // will be signed. [The Metadata of the SP will offer this info] + 'authnRequestsSigned' => false, + + // Indicates whether the messages sent by this SP + // will be signed. + 'logoutRequestSigned' => false, + + // Indicates whether the messages sent by this SP + // will be signed. + 'logoutResponseSigned' => false, + + /* Sign the Metadata + False || True (use sp certs) || array ( + 'keyFileName' => 'metadata.key', + 'certFileName' => 'metadata.crt' + ) + || array ( + 'x509cert' => '', + 'privateKey' => '' + ) + */ + 'signMetadata' => false, + + + /** signatures and encryptions required **/ + + // Indicates a requirement for the , and + // elements received by this SP to be signed. + 'wantMessagesSigned' => false, + + // Indicates a requirement for the elements received by + // this SP to be encrypted. + 'wantAssertionsEncrypted' => false, + + // Indicates a requirement for the elements received by + // this SP to be signed. [The Metadata of the SP will offer this info] + 'wantAssertionsSigned' => false, + + // Indicates a requirement for the NameID element on the SAMLResponse received + // by this SP to be present. + 'wantNameId' => true, + + // Indicates a requirement for the NameID received by + // this SP to be encrypted. + 'wantNameIdEncrypted' => false, + + // Authentication context. + // Set to false and no AuthContext will be sent in the AuthNRequest, + // Set true or don't present this parameter and you will get an AuthContext 'exact' 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' + // Set an array with the possible auth context values: array('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509'), + 'requestedAuthnContext' => false, + + // Allows the authn comparison parameter to be set, defaults to 'exact' if + // the setting is not present. + 'requestedAuthnContextComparison' => 'exact', + + // Indicates if the SP will validate all received xmls. + // (In order to validate the xml, 'strict' and 'wantXMLValidation' must be true). + 'wantXMLValidation' => true, + + // If true, SAMLResponses with an empty value at its Destination + // attribute will not be rejected for this fact. + 'relaxDestinationValidation' => false, + + // Algorithm that the toolkit will use on signing process. Options: + // 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' + // 'http://www.w3.org/2000/09/xmldsig#dsa-sha1' + // 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' + // 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384' + // 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' + // Notice that rsa-sha1 is a deprecated algorithm and should not be used + 'signatureAlgorithm' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256', + + // Algorithm that the toolkit will use on digest process. Options: + // 'http://www.w3.org/2000/09/xmldsig#sha1' + // 'http://www.w3.org/2001/04/xmlenc#sha256' + // 'http://www.w3.org/2001/04/xmldsig-more#sha384' + // 'http://www.w3.org/2001/04/xmlenc#sha512' + // Notice that sha1 is a deprecated algorithm and should not be used + 'digestAlgorithm' => 'http://www.w3.org/2001/04/xmlenc#sha256', + + // ADFS URL-Encodes SAML data as lowercase, and the toolkit by default uses + // uppercase. Turn it True for ADFS compatibility on signature verification + 'lowercaseUrlencoding' => false, + ), + + // Contact information template, it is recommended to suply a technical and support contacts + 'contactPerson' => array( + 'technical' => array( + 'givenName' => '', + 'emailAddress' => '' + ), + 'support' => array( + 'givenName' => '', + 'emailAddress' => '' + ), + ), + + // Organization information template, the info in en_US lang is recomended, add more if required + 'organization' => array( + 'en-US' => array( + 'name' => '', + 'displayname' => '', + 'url' => '' + ), + ), +); + + +/* Interoperable SAML 2.0 Web Browser SSO Profile [saml2int] http://saml2int.org/profile/current + + 'authnRequestsSigned' => false, // SP SHOULD NOT sign the , + // MUST NOT assume that the IdP validates the sign + 'wantAssertionsSigned' => true, + 'wantAssertionsEncrypted' => true, // MUST be enabled if SSL/HTTPs is disabled + 'wantNameIdEncrypted' => false, +*/ diff --git a/vendor/onelogin/php-saml/certs/README b/vendor/onelogin/php-saml/certs/README new file mode 100644 index 0000000000..1616ebda8a --- /dev/null +++ b/vendor/onelogin/php-saml/certs/README @@ -0,0 +1,14 @@ +Take care of this folder that could contain private key. Be sure that this folder never is published. + +Onelogin PHP Toolkit expects certs for the SP stored at: + + * sp.key Private Key + * sp.crt Public cert + * sp_new.crt Future Public cert + +Also you can use other cert to sign the metadata of the SP using the: + + * metadata.key + * metadata.crt + +If you are using composer to install the php-saml toolkit, You should move the certs folder to vendor/onelogin/php-saml/certs diff --git a/vendor/onelogin/php-saml/composer.json b/vendor/onelogin/php-saml/composer.json new file mode 100644 index 0000000000..2cae8af800 --- /dev/null +++ b/vendor/onelogin/php-saml/composer.json @@ -0,0 +1,34 @@ +{ + "name": "onelogin/php-saml", + "description": "OneLogin PHP SAML Toolkit", + "license": "MIT", + "homepage": "https://developers.onelogin.com/saml/php", + "keywords": ["saml", "saml2", "onelogin"], + "autoload": { + "psr-4": { + "OneLogin\\": "src/" + } + }, + "support": { + "email": "sixto.garcia@onelogin.com", + "issues": "https://github.com/onelogin/php-saml/issues", + "source": "https://github.com/onelogin/php-saml/" + }, + "require": { + "php": ">=5.4", + "robrichards/xmlseclibs": ">=3.0.4" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^1.0.2 || ^2.0", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1", + "sebastian/phpcpd": "^2.0 || ^3.0 || ^4.0", + "phploc/phploc": "^2.1 || ^3.0 || ^4.0", + "pdepend/pdepend": "^2.5.0", + "squizlabs/php_codesniffer": "^3.1.1" + }, + "suggest": { + "ext-openssl": "Install openssl lib in order to handle with x509 certs (require to support sign and encryption)", + "ext-curl": "Install curl lib to be able to use the IdPMetadataParser for parsing remote XMLs", + "ext-gettext": "Install gettext and php5-gettext libs to handle translations" + } +} diff --git a/vendor/onelogin/php-saml/phpunit.xml b/vendor/onelogin/php-saml/phpunit.xml new file mode 100644 index 0000000000..3629f2741f --- /dev/null +++ b/vendor/onelogin/php-saml/phpunit.xml @@ -0,0 +1,18 @@ + + + + ./tests/src + + + + + ./src + + + + + + + + + diff --git a/vendor/onelogin/php-saml/settings_example.php b/vendor/onelogin/php-saml/settings_example.php new file mode 100644 index 0000000000..981a21a38c --- /dev/null +++ b/vendor/onelogin/php-saml/settings_example.php @@ -0,0 +1,137 @@ + true, + + // Enable debug mode (to print errors) + 'debug' => false, + + // Set a BaseURL to be used instead of try to guess + // the BaseURL of the view that process the SAML Message. + // Ex. http://sp.example.com/ + // http://example.com/sp/ + 'baseurl' => null, + + // Service Provider Data that we are deploying + 'sp' => array( + // Identifier of the SP entity (must be a URI) + 'entityId' => '', + // Specifies info about where and how the message MUST be + // returned to the requester, in this case our SP. + 'assertionConsumerService' => array( + // URL Location where the from the IdP will be returned + 'url' => '', + // SAML protocol binding to be used when returning the + // message. Onelogin Toolkit supports for this endpoint the + // HTTP-POST binding only + 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + ), + // If you need to specify requested attributes, set a + // attributeConsumingService. nameFormat, attributeValue and + // friendlyName can be omitted. Otherwise remove this section. + "attributeConsumingService"=> array( + "serviceName" => "SP test", + "serviceDescription" => "Test Service", + "requestedAttributes" => array( + array( + "name" => "", + "isRequired" => false, + "nameFormat" => "", + "friendlyName" => "", + "attributeValue" => "" + ) + ) + ), + // Specifies info about where and how the message MUST be + // returned to the requester, in this case our SP. + 'singleLogoutService' => array( + // URL Location where the from the IdP will be returned + 'url' => '', + // SAML protocol binding to be used when returning the + // message. Onelogin Toolkit supports for this endpoint the + // HTTP-Redirect binding only + 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', + ), + // Specifies constraints on the name identifier to be used to + // represent the requested subject. + // Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported + 'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified', + + // Usually x509cert and privateKey of the SP are provided by files placed at + // the certs folder. But we can also provide them with the following parameters + 'x509cert' => '', + 'privateKey' => '', + + /* + * Key rollover + * If you plan to update the SP x509cert and privateKey + * you can define here the new x509cert and it will be + * published on the SP metadata so Identity Providers can + * read them and get ready for rollover. + */ + // 'x509certNew' => '', + ), + + // Identity Provider Data that we want connect with our SP + 'idp' => array( + // Identifier of the IdP entity (must be a URI) + 'entityId' => '', + // SSO endpoint info of the IdP. (Authentication Request protocol) + 'singleSignOnService' => array( + // URL Target of the IdP where the SP will send the Authentication Request Message + 'url' => '', + // SAML protocol binding to be used when returning the + // message. Onelogin Toolkit supports for this endpoint the + // HTTP-Redirect binding only + 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', + ), + // SLO endpoint info of the IdP. + 'singleLogoutService' => array( + // URL Location of the IdP where the SP will send the SLO Request + 'url' => '', + // URL location of the IdP where the SP SLO Response will be sent (ResponseLocation) + // if not set, url for the SLO Request will be used + 'responseUrl' => '', + // SAML protocol binding to be used when returning the + // message. Onelogin Toolkit supports for this endpoint the + // HTTP-Redirect binding only + 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', + ), + // Public x509 certificate of the IdP + 'x509cert' => '', + /* + * Instead of use the whole x509cert you can use a fingerprint in + * order to validate the SAMLResponse, but we don't recommend to use + * that method on production since is exploitable by a collision + * attack. + * (openssl x509 -noout -fingerprint -in "idp.crt" to generate it, + * or add for example the -sha256 , -sha384 or -sha512 parameter) + * + * If a fingerprint is provided, then the certFingerprintAlgorithm is required in order to + * let the toolkit know which Algorithm was used. Possible values: sha1, sha256, sha384 or sha512 + * 'sha1' is the default value. + */ + // 'certFingerprint' => '', + // 'certFingerprintAlgorithm' => 'sha1', + + /* In some scenarios the IdP uses different certificates for + * signing/encryption, or is under key rollover phase and more + * than one certificate is published on IdP metadata. + * In order to handle that the toolkit offers that parameter. + * (when used, 'x509cert' and 'certFingerprint' values are + * ignored). + */ + // 'x509certMulti' => array( + // 'signing' => array( + // 0 => '', + // ), + // 'encryption' => array( + // 0 => '', + // ) + // ), + ), +); diff --git a/vendor/onelogin/php-saml/src/Saml2/Auth.php b/vendor/onelogin/php-saml/src/Saml2/Auth.php new file mode 100644 index 0000000000..42efb64efe --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/Auth.php @@ -0,0 +1,815 @@ + + * @license MIT https://github.com/onelogin/php-saml/blob/master/LICENSE + * @link https://github.com/onelogin/php-saml + */ + +namespace OneLogin\Saml2; + +use RobRichards\XMLSecLibs\XMLSecurityKey; + +use Exception; + +/** + * Main class of OneLogin's PHP Toolkit + */ +class Auth +{ + /** + * Settings data. + * + * @var Settings + */ + private $_settings; + + /** + * User attributes data. + * + * @var array + */ + private $_attributes = array(); + + /** + * User attributes data with FriendlyName index. + * + * @var array + */ + private $_attributesWithFriendlyName = array(); + + /** + * NameID + * + * @var string + */ + private $_nameid; + + /** + * NameID Format + * + * @var string + */ + private $_nameidFormat; + + /** + * NameID NameQualifier + * + * @var string + */ + private $_nameidNameQualifier; + + /** + * NameID SP NameQualifier + * + * @var string + */ + private $_nameidSPNameQualifier; + + /** + * If user is authenticated. + * + * @var bool + */ + private $_authenticated = false; + + + /** + * SessionIndex. When the user is logged, this stored it + * from the AuthnStatement of the SAML Response + * + * @var string + */ + private $_sessionIndex; + + /** + * SessionNotOnOrAfter. When the user is logged, this stored it + * from the AuthnStatement of the SAML Response + * + * @var int|null + */ + private $_sessionExpiration; + + /** + * The ID of the last message processed + * + * @var string + */ + private $_lastMessageId; + + /** + * The ID of the last assertion processed + * + * @var string + */ + private $_lastAssertionId; + + /** + * The NotOnOrAfter value of the valid SubjectConfirmationData + * node (if any) of the last assertion processed + * + * @var int + */ + private $_lastAssertionNotOnOrAfter; + + /** + * If any error. + * + * @var array + */ + private $_errors = array(); + + /** + * Last error object. + * + * @var Error|null + */ + private $_lastErrorException; + + /** + * Last error. + * + * @var string|null + */ + private $_lastError; + + /** + * Last AuthNRequest ID or LogoutRequest ID generated by this Service Provider + * + * @var string + */ + private $_lastRequestID; + + /** + * The most recently-constructed/processed XML SAML request + * (AuthNRequest, LogoutRequest) + * + * @var string + */ + private $_lastRequest; + + /** + * The most recently-constructed/processed XML SAML response + * (SAMLResponse, LogoutResponse). If the SAMLResponse was + * encrypted, by default tries to return the decrypted XML + * + * @var string|\DomDocument|null + */ + private $_lastResponse; + + /** + * Initializes the SP SAML instance. + * + * @param array|null $settings Setting data + * + * @throws Exception + * @throws Error + */ + public function __construct(array $settings = null) + { + $this->_settings = new Settings($settings); + } + + /** + * Returns the settings info + * + * @return Settings The settings data. + */ + public function getSettings() + { + return $this->_settings; + } + + /** + * Set the strict mode active/disable + * + * @param bool $value Strict parameter + * + * @throws Error + */ + public function setStrict($value) + { + if (!is_bool($value)) { + throw new Error( + 'Invalid value passed to setStrict()', + Error::SETTINGS_INVALID_SYNTAX + ); + } + + $this->_settings->setStrict($value); + } + + /** + * Process the SAML Response sent by the IdP. + * + * @param string|null $requestId The ID of the AuthNRequest sent by this SP to the IdP + * + * @throws Error + * @throws ValidationError + */ + public function processResponse($requestId = null) + { + $this->_errors = array(); + $this->_lastError = $this->_lastErrorException = null; + if (isset($_POST['SAMLResponse'])) { + // AuthnResponse -- HTTP_POST Binding + $response = new Response($this->_settings, $_POST['SAMLResponse']); + $this->_lastResponse = $response->getXMLDocument(); + + if ($response->isValid($requestId)) { + $this->_attributes = $response->getAttributes(); + $this->_attributesWithFriendlyName = $response->getAttributesWithFriendlyName(); + $this->_nameid = $response->getNameId(); + $this->_nameidFormat = $response->getNameIdFormat(); + $this->_nameidNameQualifier = $response->getNameIdNameQualifier(); + $this->_nameidSPNameQualifier = $response->getNameIdSPNameQualifier(); + $this->_authenticated = true; + $this->_sessionIndex = $response->getSessionIndex(); + $this->_sessionExpiration = $response->getSessionNotOnOrAfter(); + $this->_lastMessageId = $response->getId(); + $this->_lastAssertionId = $response->getAssertionId(); + $this->_lastAssertionNotOnOrAfter = $response->getAssertionNotOnOrAfter(); + } else { + $this->_errors[] = 'invalid_response'; + $this->_lastErrorException = $response->getErrorException(); + $this->_lastError = $response->getError(); + } + } else { + $this->_errors[] = 'invalid_binding'; + throw new Error( + 'SAML Response not found, Only supported HTTP_POST Binding', + Error::SAML_RESPONSE_NOT_FOUND + ); + } + } + + /** + * Process the SAML Logout Response / Logout Request sent by the IdP. + * + * @param bool $keepLocalSession When false will destroy the local session, otherwise will keep it + * @param string|null $requestId The ID of the LogoutRequest sent by this SP to the IdP + * @param bool $retrieveParametersFromServer True if we want to use parameters from $_SERVER to validate the signature + * @param callable $cbDeleteSession Callback to be executed to delete session + * @param bool $stay True if we want to stay (returns the url string) False to redirect + * + * @return string|null + * + * @throws Error + */ + public function processSLO($keepLocalSession = false, $requestId = null, $retrieveParametersFromServer = false, $cbDeleteSession = null, $stay = false) + { + $this->_errors = array(); + $this->_lastError = $this->_lastErrorException = null; + if (isset($_GET['SAMLResponse'])) { + $logoutResponse = new LogoutResponse($this->_settings, $_GET['SAMLResponse']); + $this->_lastResponse = $logoutResponse->getXML(); + if (!$logoutResponse->isValid($requestId, $retrieveParametersFromServer)) { + $this->_errors[] = 'invalid_logout_response'; + $this->_lastErrorException = $logoutResponse->getErrorException(); + $this->_lastError = $logoutResponse->getError(); + + } else if ($logoutResponse->getStatus() !== Constants::STATUS_SUCCESS) { + $this->_errors[] = 'logout_not_success'; + } else { + $this->_lastMessageId = $logoutResponse->id; + if (!$keepLocalSession) { + if ($cbDeleteSession === null) { + Utils::deleteLocalSession(); + } else { + call_user_func($cbDeleteSession); + } + } + } + } else if (isset($_GET['SAMLRequest'])) { + $logoutRequest = new LogoutRequest($this->_settings, $_GET['SAMLRequest']); + $this->_lastRequest = $logoutRequest->getXML(); + if (!$logoutRequest->isValid($retrieveParametersFromServer)) { + $this->_errors[] = 'invalid_logout_request'; + $this->_lastErrorException = $logoutRequest->getErrorException(); + $this->_lastError = $logoutRequest->getError(); + } else { + if (!$keepLocalSession) { + if ($cbDeleteSession === null) { + Utils::deleteLocalSession(); + } else { + call_user_func($cbDeleteSession); + } + } + $inResponseTo = $logoutRequest->id; + $this->_lastMessageId = $logoutRequest->id; + $responseBuilder = new LogoutResponse($this->_settings); + $responseBuilder->build($inResponseTo); + $this->_lastResponse = $responseBuilder->getXML(); + + $logoutResponse = $responseBuilder->getResponse(); + + $parameters = array('SAMLResponse' => $logoutResponse); + if (isset($_GET['RelayState'])) { + $parameters['RelayState'] = $_GET['RelayState']; + } + + $security = $this->_settings->getSecurityData(); + if (isset($security['logoutResponseSigned']) && $security['logoutResponseSigned']) { + $signature = $this->buildResponseSignature($logoutResponse, isset($parameters['RelayState'])? $parameters['RelayState']: null, $security['signatureAlgorithm']); + $parameters['SigAlg'] = $security['signatureAlgorithm']; + $parameters['Signature'] = $signature; + } + + return $this->redirectTo($this->getSLOResponseUrl(), $parameters, $stay); + } + } else { + $this->_errors[] = 'invalid_binding'; + throw new Error( + 'SAML LogoutRequest/LogoutResponse not found. Only supported HTTP_REDIRECT Binding', + Error::SAML_LOGOUTMESSAGE_NOT_FOUND + ); + } + } + + /** + * Redirects the user to the url past by parameter + * or to the url that we defined in our SSO Request. + * + * @param string $url The target URL to redirect the user. + * @param array $parameters Extra parameters to be passed as part of the url + * @param bool $stay True if we want to stay (returns the url string) False to redirect + * + * @return string|null + */ + public function redirectTo($url = '', array $parameters = array(), $stay = false) + { + assert(is_string($url)); + + if (empty($url) && isset($_REQUEST['RelayState'])) { + $url = $_REQUEST['RelayState']; + } + + return Utils::redirect($url, $parameters, $stay); + } + + /** + * Checks if the user is authenticated or not. + * + * @return bool True if the user is authenticated + */ + public function isAuthenticated() + { + return $this->_authenticated; + } + + /** + * Returns the set of SAML attributes. + * + * @return array Attributes of the user. + */ + public function getAttributes() + { + return $this->_attributes; + } + + + /** + * Returns the set of SAML attributes indexed by FriendlyName + * + * @return array Attributes of the user. + */ + public function getAttributesWithFriendlyName() + { + return $this->_attributesWithFriendlyName; + } + + /** + * Returns the nameID + * + * @return string The nameID of the assertion + */ + public function getNameId() + { + return $this->_nameid; + } + + /** + * Returns the nameID Format + * + * @return string The nameID Format of the assertion + */ + public function getNameIdFormat() + { + return $this->_nameidFormat; + } + + /** + * Returns the nameID NameQualifier + * + * @return string The nameID NameQualifier of the assertion + */ + public function getNameIdNameQualifier() + { + return $this->_nameidNameQualifier; + } + + /** + * Returns the nameID SP NameQualifier + * + * @return string The nameID SP NameQualifier of the assertion + */ + public function getNameIdSPNameQualifier() + { + return $this->_nameidSPNameQualifier; + } + + /** + * Returns the SessionIndex + * + * @return string|null The SessionIndex of the assertion + */ + public function getSessionIndex() + { + return $this->_sessionIndex; + } + + /** + * Returns the SessionNotOnOrAfter + * + * @return int|null The SessionNotOnOrAfter of the assertion + */ + public function getSessionExpiration() + { + return $this->_sessionExpiration; + } + + /** + * Returns if there were any error + * + * @return array Errors + */ + public function getErrors() + { + return $this->_errors; + } + + /** + * Returns the reason for the last error + * + * @return string|null Error reason + */ + public function getLastErrorReason() + { + return $this->_lastError; + } + + + /** + * Returns the last error + * + * @return Exception|null Error + */ + public function getLastErrorException() + { + return $this->_lastErrorException; + } + + /** + * Returns the requested SAML attribute + * + * @param string $name The requested attribute of the user. + * + * @return array|null Requested SAML attribute ($name). + */ + public function getAttribute($name) + { + assert(is_string($name)); + + $value = null; + if (isset($this->_attributes[$name])) { + return $this->_attributes[$name]; + } + return $value; + } + + /** + * Returns the requested SAML attribute indexed by FriendlyName + * + * @param string $friendlyName The requested attribute of the user. + * + * @return array|null Requested SAML attribute ($friendlyName). + */ + public function getAttributeWithFriendlyName($friendlyName) + { + assert(is_string($friendlyName)); + $value = null; + if (isset($this->_attributesWithFriendlyName[$friendlyName])) { + return $this->_attributesWithFriendlyName[$friendlyName]; + } + return $value; + } + + /** + * Initiates the SSO process. + * + * @param string|null $returnTo The target URL the user should be returned to after login. + * @param array $parameters Extra parameters to be added to the GET + * @param bool $forceAuthn When true the AuthNRequest will set the ForceAuthn='true' + * @param bool $isPassive When true the AuthNRequest will set the Ispassive='true' + * @param bool $stay True if we want to stay (returns the url string) False to redirect + * @param bool $setNameIdPolicy When true the AuthNRequest will set a nameIdPolicy element + * @param string $nameIdValueReq Indicates to the IdP the subject that should be authenticated + * + * @return string|null If $stay is True, it return a string with the SLO URL + LogoutRequest + parameters + * + * @throws Error + */ + public function login($returnTo = null, array $parameters = array(), $forceAuthn = false, $isPassive = false, $stay = false, $setNameIdPolicy = true, $nameIdValueReq = null) + { + $authnRequest = $this->buildAuthnRequest($this->_settings, $forceAuthn, $isPassive, $setNameIdPolicy, $nameIdValueReq); + + $this->_lastRequest = $authnRequest->getXML(); + $this->_lastRequestID = $authnRequest->getId(); + + $samlRequest = $authnRequest->getRequest(); + $parameters['SAMLRequest'] = $samlRequest; + + if (!empty($returnTo)) { + $parameters['RelayState'] = $returnTo; + } else { + $parameters['RelayState'] = Utils::getSelfRoutedURLNoQuery(); + } + + $security = $this->_settings->getSecurityData(); + if (isset($security['authnRequestsSigned']) && $security['authnRequestsSigned']) { + $signature = $this->buildRequestSignature($samlRequest, $parameters['RelayState'], $security['signatureAlgorithm']); + $parameters['SigAlg'] = $security['signatureAlgorithm']; + $parameters['Signature'] = $signature; + } + return $this->redirectTo($this->getSSOurl(), $parameters, $stay); + } + + /** + * Initiates the SLO process. + * + * @param string|null $returnTo The target URL the user should be returned to after logout. + * @param array $parameters Extra parameters to be added to the GET + * @param string|null $nameId The NameID that will be set in the LogoutRequest. + * @param string|null $sessionIndex The SessionIndex (taken from the SAML Response in the SSO process). + * @param bool $stay True if we want to stay (returns the url string) False to redirect + * @param string|null $nameIdFormat The NameID Format will be set in the LogoutRequest. + * @param string|null $nameIdNameQualifier The NameID NameQualifier will be set in the LogoutRequest. + * + * @return string|null If $stay is True, it return a string with the SLO URL + LogoutRequest + parameters + * + * @throws Error + */ + public function logout($returnTo = null, array $parameters = array(), $nameId = null, $sessionIndex = null, $stay = false, $nameIdFormat = null, $nameIdNameQualifier = null, $nameIdSPNameQualifier = null) + { + $sloUrl = $this->getSLOurl(); + if (empty($sloUrl)) { + throw new Error( + 'The IdP does not support Single Log Out', + Error::SAML_SINGLE_LOGOUT_NOT_SUPPORTED + ); + } + + if (empty($nameId) && !empty($this->_nameid)) { + $nameId = $this->_nameid; + } + if (empty($nameIdFormat) && !empty($this->_nameidFormat)) { + $nameIdFormat = $this->_nameidFormat; + } + + $logoutRequest = new LogoutRequest($this->_settings, null, $nameId, $sessionIndex, $nameIdFormat, $nameIdNameQualifier, $nameIdSPNameQualifier); + + $this->_lastRequest = $logoutRequest->getXML(); + $this->_lastRequestID = $logoutRequest->id; + + $samlRequest = $logoutRequest->getRequest(); + + $parameters['SAMLRequest'] = $samlRequest; + if (!empty($returnTo)) { + $parameters['RelayState'] = $returnTo; + } else { + $parameters['RelayState'] = Utils::getSelfRoutedURLNoQuery(); + } + + $security = $this->_settings->getSecurityData(); + if (isset($security['logoutRequestSigned']) && $security['logoutRequestSigned']) { + $signature = $this->buildRequestSignature($samlRequest, $parameters['RelayState'], $security['signatureAlgorithm']); + $parameters['SigAlg'] = $security['signatureAlgorithm']; + $parameters['Signature'] = $signature; + } + + return $this->redirectTo($sloUrl, $parameters, $stay); + } + + /** + * Gets the SSO url. + * + * @return string The url of the Single Sign On Service + */ + public function getSSOurl() + { + $idpData = $this->_settings->getIdPData(); + return $idpData['singleSignOnService']['url']; + } + + /** + * Gets the SLO url. + * + * @return string|null The url of the Single Logout Service + */ + public function getSLOurl() + { + $url = null; + $idpData = $this->_settings->getIdPData(); + if (isset($idpData['singleLogoutService']) && isset($idpData['singleLogoutService']['url'])) { + $url = $idpData['singleLogoutService']['url']; + } + return $url; + } + + /** + * Gets the SLO response url. + * + * @return string|null The response url of the Single Logout Service + */ + public function getSLOResponseUrl() + { + $idpData = $this->_settings->getIdPData(); + if (isset($idpData['singleLogoutService']) && isset($idpData['singleLogoutService']['responseUrl'])) { + return $idpData['singleLogoutService']['responseUrl']; + } + return $this->getSLOurl(); + } + + /** + * Gets the ID of the last AuthNRequest or LogoutRequest generated by the Service Provider. + * + * @return string The ID of the Request SAML message. + */ + public function getLastRequestID() + { + return $this->_lastRequestID; + } + + /** + * Creates an AuthnRequest + * + * @param Settings $settings Setting data + * @param bool $forceAuthn When true the AuthNRequest will set the ForceAuthn='true' + * @param bool $isPassive When true the AuthNRequest will set the Ispassive='true' + * @param bool $setNameIdPolicy When true the AuthNRequest will set a nameIdPolicy element + * @param string $nameIdValueReq Indicates to the IdP the subject that should be authenticated + * + * @return AuthnRequest The AuthnRequest object + */ + public function buildAuthnRequest($settings, $forceAuthn, $isPassive, $setNameIdPolicy, $nameIdValueReq = null) + { + return new AuthnRequest($settings, $forceAuthn, $isPassive, $setNameIdPolicy, $nameIdValueReq); + } + + /** + * Generates the Signature for a SAML Request + * + * @param string $samlRequest The SAML Request + * @param string $relayState The RelayState + * @param string $signAlgorithm Signature algorithm method + * + * @return string A base64 encoded signature + * + * @throws Exception + * @throws Error + */ + public function buildRequestSignature($samlRequest, $relayState, $signAlgorithm = XMLSecurityKey::RSA_SHA256) + { + return $this->buildMessageSignature($samlRequest, $relayState, $signAlgorithm, "SAMLRequest"); + } + + /** + * Generates the Signature for a SAML Response + * + * @param string $samlResponse The SAML Response + * @param string $relayState The RelayState + * @param string $signAlgorithm Signature algorithm method + * + * @return string A base64 encoded signature + * + * @throws Exception + * @throws Error + */ + public function buildResponseSignature($samlResponse, $relayState, $signAlgorithm = XMLSecurityKey::RSA_SHA256) + { + return $this->buildMessageSignature($samlResponse, $relayState, $signAlgorithm, "SAMLResponse"); + } + + /** + * Generates the Signature for a SAML Message + * + * @param string $samlMessage The SAML Message + * @param string $relayState The RelayState + * @param string $signAlgorithm Signature algorithm method + * @param string $type "SAMLRequest" or "SAMLResponse" + * + * @return string A base64 encoded signature + * + * @throws Exception + * @throws Error + */ + private function buildMessageSignature($samlMessage, $relayState, $signAlgorithm = XMLSecurityKey::RSA_SHA256, $type = "SAMLRequest") + { + $key = $this->_settings->getSPkey(); + if (empty($key)) { + if ($type == "SAMLRequest") { + $errorMsg = "Trying to sign the SAML Request but can't load the SP private key"; + } else { + $errorMsg = "Trying to sign the SAML Response but can't load the SP private key"; + } + + throw new Error($errorMsg, Error::PRIVATE_KEY_NOT_FOUND); + } + + $objKey = new XMLSecurityKey($signAlgorithm, array('type' => 'private')); + $objKey->loadKey($key, false); + + $security = $this->_settings->getSecurityData(); + if ($security['lowercaseUrlencoding']) { + $msg = $type.'='.rawurlencode($samlMessage); + if (isset($relayState)) { + $msg .= '&RelayState='.rawurlencode($relayState); + } + $msg .= '&SigAlg=' . rawurlencode($signAlgorithm); + } else { + $msg = $type.'='.urlencode($samlMessage); + if (isset($relayState)) { + $msg .= '&RelayState='.urlencode($relayState); + } + $msg .= '&SigAlg=' . urlencode($signAlgorithm); + } + $signature = $objKey->signData($msg); + return base64_encode($signature); + } + + /** + * @return string The ID of the last message processed + */ + public function getLastMessageId() + { + return $this->_lastMessageId; + } + + /** + * @return string The ID of the last assertion processed + */ + public function getLastAssertionId() + { + return $this->_lastAssertionId; + } + + /** + * @return int The NotOnOrAfter value of the valid + * SubjectConfirmationData node (if any) + * of the last assertion processed + */ + public function getLastAssertionNotOnOrAfter() + { + return $this->_lastAssertionNotOnOrAfter; + } + + /** + * Returns the most recently-constructed/processed + * XML SAML request (AuthNRequest, LogoutRequest) + * + * @return string|null The Request XML + */ + public function getLastRequestXML() + { + return $this->_lastRequest; + } + + /** + * Returns the most recently-constructed/processed + * XML SAML response (SAMLResponse, LogoutResponse). + * If the SAMLResponse was encrypted, by default tries + * to return the decrypted XML. + * + * @return string|null The Response XML + */ + public function getLastResponseXML() + { + $response = null; + if (isset($this->_lastResponse)) { + if (is_string($this->_lastResponse)) { + $response = $this->_lastResponse; + } else { + $response = $this->_lastResponse->saveXML(); + } + } + + return $response; + } +} diff --git a/vendor/onelogin/php-saml/src/Saml2/AuthnRequest.php b/vendor/onelogin/php-saml/src/Saml2/AuthnRequest.php new file mode 100644 index 0000000000..a1311f71ef --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/AuthnRequest.php @@ -0,0 +1,214 @@ + + * @license MIT https://github.com/onelogin/php-saml/blob/master/LICENSE + * @link https://github.com/onelogin/php-saml + */ + +namespace OneLogin\Saml2; + +/** + * SAML 2 Authentication Request + */ +class AuthnRequest +{ + /** + * Object that represents the setting info + * + * @var Settings + */ + protected $_settings; + + /** + * SAML AuthNRequest string + * + * @var string + */ + private $_authnRequest; + + /** + * SAML AuthNRequest ID. + * + * @var string + */ + private $_id; + + /** + * Constructs the AuthnRequest object. + * + * @param Settings $settings SAML Toolkit Settings + * @param bool $forceAuthn When true the AuthNReuqest will set the ForceAuthn='true' + * @param bool $isPassive When true the AuthNReuqest will set the Ispassive='true' + * @param bool $setNameIdPolicy When true the AuthNReuqest will set a nameIdPolicy + * @param string $nameIdValueReq Indicates to the IdP the subject that should be authenticated + */ + public function __construct(\OneLogin\Saml2\Settings $settings, $forceAuthn = false, $isPassive = false, $setNameIdPolicy = true, $nameIdValueReq = null) + { + $this->_settings = $settings; + + $spData = $this->_settings->getSPData(); + $idpData = $this->_settings->getIdPData(); + $security = $this->_settings->getSecurityData(); + + $id = Utils::generateUniqueID(); + $issueInstant = Utils::parseTime2SAML(time()); + + $subjectStr = ""; + if (isset($nameIdValueReq)) { + $subjectStr = << + {$nameIdValueReq} + + +SUBJECT; + } + + $nameIdPolicyStr = ''; + if ($setNameIdPolicy) { + $nameIDPolicyFormat = $spData['NameIDFormat']; + if (isset($security['wantNameIdEncrypted']) && $security['wantNameIdEncrypted']) { + $nameIDPolicyFormat = Constants::NAMEID_ENCRYPTED; + } + + $nameIdPolicyStr = << +NAMEIDPOLICY; + } + + + $providerNameStr = ''; + $organizationData = $settings->getOrganization(); + if (!empty($organizationData)) { + $langs = array_keys($organizationData); + if (in_array('en-US', $langs)) { + $lang = 'en-US'; + } else { + $lang = $langs[0]; + } + if (isset($organizationData[$lang]['displayname']) && !empty($organizationData[$lang]['displayname'])) { + $providerNameStr = << + urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport + +REQUESTEDAUTHN; + } else { + $requestedAuthnStr .= " \n"; + foreach ($security['requestedAuthnContext'] as $contextValue) { + $requestedAuthnStr .= " ".$contextValue."\n"; + } + $requestedAuthnStr .= ' '; + } + } + + $spEntityId = htmlspecialchars($spData['entityId'], ENT_QUOTES); + $acsUrl = htmlspecialchars($spData['assertionConsumerService']['url'], ENT_QUOTES); + $request = << + {$spEntityId}{$subjectStr}{$nameIdPolicyStr}{$requestedAuthnStr} + +AUTHNREQUEST; + + $this->_id = $id; + $this->_authnRequest = $request; + } + + /** + * Returns deflated, base64 encoded, unsigned AuthnRequest. + * + * @param bool|null $deflate Whether or not we should 'gzdeflate' the request body before we return it. + * + * @return string + */ + public function getRequest($deflate = null) + { + $subject = $this->_authnRequest; + + if (is_null($deflate)) { + $deflate = $this->_settings->shouldCompressRequests(); + } + + if ($deflate) { + $subject = gzdeflate($this->_authnRequest); + } + + $base64Request = base64_encode($subject); + return $base64Request; + } + + /** + * Returns the AuthNRequest ID. + * + * @return string + */ + public function getId() + { + return $this->_id; + } + + /** + * Returns the XML that will be sent as part of the request + * + * @return string + */ + public function getXML() + { + return $this->_authnRequest; + } +} diff --git a/vendor/onelogin/php-saml/src/Saml2/Constants.php b/vendor/onelogin/php-saml/src/Saml2/Constants.php new file mode 100644 index 0000000000..21261fb247 --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/Constants.php @@ -0,0 +1,84 @@ + + * @license MIT https://github.com/onelogin/php-saml/blob/master/LICENSE + * @link https://github.com/onelogin/php-saml + */ + +namespace OneLogin\Saml2; + +/** + * Constants of OneLogin PHP Toolkit + * + * Defines all required constants + */ +class Constants +{ + // Value added to the current time in time condition validations + const ALLOWED_CLOCK_DRIFT = 180; // 3 min in seconds + + // NameID Formats + const NAMEID_EMAIL_ADDRESS = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'; + const NAMEID_X509_SUBJECT_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName'; + const NAMEID_WINDOWS_DOMAIN_QUALIFIED_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName'; + const NAMEID_UNSPECIFIED = 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified'; + const NAMEID_KERBEROS = 'urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos'; + const NAMEID_ENTITY = 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity'; + const NAMEID_TRANSIENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'; + const NAMEID_PERSISTENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'; + const NAMEID_ENCRYPTED = 'urn:oasis:names:tc:SAML:2.0:nameid-format:encrypted'; + + // Attribute Name Formats + const ATTRNAME_FORMAT_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified'; + const ATTRNAME_FORMAT_URI = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri'; + const ATTRNAME_FORMAT_BASIC = 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic'; + + // Namespaces + const NS_SAML = 'urn:oasis:names:tc:SAML:2.0:assertion'; + const NS_SAMLP = 'urn:oasis:names:tc:SAML:2.0:protocol'; + const NS_SOAP = 'http://schemas.xmlsoap.org/soap/envelope/'; + const NS_MD = 'urn:oasis:names:tc:SAML:2.0:metadata'; + const NS_XS = 'http://www.w3.org/2001/XMLSchema'; + const NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'; + const NS_XENC = 'http://www.w3.org/2001/04/xmlenc#'; + const NS_DS = 'http://www.w3.org/2000/09/xmldsig#'; + + // Bindings + const BINDING_HTTP_POST = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'; + const BINDING_HTTP_REDIRECT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'; + const BINDING_HTTP_ARTIFACT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact'; + const BINDING_SOAP = 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP'; + const BINDING_DEFLATE = 'urn:oasis:names:tc:SAML:2.0:bindings:URL-Encoding:DEFLATE'; + + // Auth Context Class + const AC_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified'; + const AC_PASSWORD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password'; + const AC_PASSWORD_PROTECTED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'; + const AC_X509 = 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509'; + const AC_SMARTCARD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Smartcard'; + const AC_KERBEROS = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos'; + const AC_WINDOWS = 'urn:federation:authentication:windows'; + const AC_TLS = 'urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient'; + + // Subject Confirmation + const CM_BEARER = 'urn:oasis:names:tc:SAML:2.0:cm:bearer'; + const CM_HOLDER_KEY = 'urn:oasis:names:tc:SAML:2.0:cm:holder-of-key'; + const CM_SENDER_VOUCHES = 'urn:oasis:names:tc:SAML:2.0:cm:sender-vouches'; + + // Status Codes + const STATUS_SUCCESS = 'urn:oasis:names:tc:SAML:2.0:status:Success'; + const STATUS_REQUESTER = 'urn:oasis:names:tc:SAML:2.0:status:Requester'; + const STATUS_RESPONDER = 'urn:oasis:names:tc:SAML:2.0:status:Responder'; + const STATUS_VERSION_MISMATCH = 'urn:oasis:names:tc:SAML:2.0:status:VersionMismatch'; + const STATUS_NO_PASSIVE = 'urn:oasis:names:tc:SAML:2.0:status:NoPassive'; + const STATUS_PARTIAL_LOGOUT = 'urn:oasis:names:tc:SAML:2.0:status:PartialLogout'; + const STATUS_PROXY_COUNT_EXCEEDED = 'urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded'; +} diff --git a/vendor/onelogin/php-saml/src/Saml2/Error.php b/vendor/onelogin/php-saml/src/Saml2/Error.php new file mode 100644 index 0000000000..211acf4865 --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/Error.php @@ -0,0 +1,66 @@ + + * @license MIT https://github.com/onelogin/php-saml/blob/master/LICENSE + * @link https://github.com/onelogin/php-saml + */ + +namespace OneLogin\Saml2; + +use Exception; + +/** + * Error class of OneLogin PHP Toolkit + * + * Defines the Error class + */ +class Error extends Exception +{ + // Errors + const SETTINGS_FILE_NOT_FOUND = 0; + const SETTINGS_INVALID_SYNTAX = 1; + const SETTINGS_INVALID = 2; + const METADATA_SP_INVALID = 3; + const SP_CERTS_NOT_FOUND = 4; + // SP_CERTS_NOT_FOUND is deprecated, use CERT_NOT_FOUND instead + const CERT_NOT_FOUND = 4; + const REDIRECT_INVALID_URL = 5; + const PUBLIC_CERT_FILE_NOT_FOUND = 6; + const PRIVATE_KEY_FILE_NOT_FOUND = 7; + const SAML_RESPONSE_NOT_FOUND = 8; + const SAML_LOGOUTMESSAGE_NOT_FOUND = 9; + const SAML_LOGOUTREQUEST_INVALID = 10; + const SAML_LOGOUTRESPONSE_INVALID = 11; + const SAML_SINGLE_LOGOUT_NOT_SUPPORTED = 12; + const PRIVATE_KEY_NOT_FOUND = 13; + const UNSUPPORTED_SETTINGS_OBJECT = 14; + + /** + * Constructor + * + * @param string $msg Describes the error. + * @param int $code The code error (defined in the error class). + * @param array|null $args Arguments used in the message that describes the error. + */ + public function __construct($msg, $code = 0, $args = array()) + { + assert(is_string($msg)); + assert(is_int($code)); + + if (!isset($args)) { + $args = array(); + } + $params = array_merge(array($msg), $args); + $message = call_user_func_array('sprintf', $params); + + parent::__construct($message, $code); + } +} diff --git a/vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php b/vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php new file mode 100644 index 0000000000..422dcf1421 --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/IdPMetadataParser.php @@ -0,0 +1,240 @@ + + * @license MIT https://github.com/onelogin/php-saml/blob/master/LICENSE + * @link https://github.com/onelogin/php-saml + */ + +namespace OneLogin\Saml2; + +use DOMDocument; +use Exception; + +/** + * IdP Metadata Parser of OneLogin PHP Toolkit + */ +class IdPMetadataParser +{ + /** + * Get IdP Metadata Info from URL + * + * @param string $url URL where the IdP metadata is published + * @param string $entityId Entity Id of the desired IdP, if no + * entity Id is provided and the XML + * metadata contains more than one + * IDPSSODescriptor, the first is returned + * @param string $desiredNameIdFormat If available on IdP metadata, use that nameIdFormat + * @param string $desiredSSOBinding Parse specific binding SSO endpoint + * @param string $desiredSLOBinding Parse specific binding SLO endpoint + * + * @return array metadata info in php-saml settings format + */ + public static function parseRemoteXML($url, $entityId = null, $desiredNameIdFormat = null, $desiredSSOBinding = Constants::BINDING_HTTP_REDIRECT, $desiredSLOBinding = Constants::BINDING_HTTP_REDIRECT) + { + $metadataInfo = array(); + + try { + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + curl_setopt($ch, CURLOPT_FAILONERROR, 1); + + $xml = curl_exec($ch); + if ($xml !== false) { + $metadataInfo = self::parseXML($xml, $entityId, $desiredNameIdFormat, $desiredSSOBinding, $desiredSLOBinding); + } else { + throw new Exception(curl_error($ch), curl_errno($ch)); + } + } catch (Exception $e) { + throw new Exception('Error on parseRemoteXML. '.$e->getMessage()); + } + return $metadataInfo; + } + + /** + * Get IdP Metadata Info from File + * + * @param string $filepath File path + * @param string $entityId Entity Id of the desired IdP, if no + * entity Id is provided and the XML + * metadata contains more than one + * IDPSSODescriptor, the first is returned + * @param string $desiredNameIdFormat If available on IdP metadata, use that nameIdFormat + * @param string $desiredSSOBinding Parse specific binding SSO endpoint + * @param string $desiredSLOBinding Parse specific binding SLO endpoint + * + * @return array metadata info in php-saml settings format + */ + public static function parseFileXML($filepath, $entityId = null, $desiredNameIdFormat = null, $desiredSSOBinding = Constants::BINDING_HTTP_REDIRECT, $desiredSLOBinding = Constants::BINDING_HTTP_REDIRECT) + { + $metadataInfo = array(); + + try { + if (file_exists($filepath)) { + $data = file_get_contents($filepath); + $metadataInfo = self::parseXML($data, $entityId, $desiredNameIdFormat, $desiredSSOBinding, $desiredSLOBinding); + } + } catch (Exception $e) { + throw new Exception('Error on parseFileXML. '.$e->getMessage()); + } + return $metadataInfo; + } + + /** + * Get IdP Metadata Info from URL + * + * @param string $xml XML that contains IdP metadata + * @param string $entityId Entity Id of the desired IdP, if no + * entity Id is provided and the XML + * metadata contains more than one + * IDPSSODescriptor, the first is returned + * @param string $desiredNameIdFormat If available on IdP metadata, use that nameIdFormat + * @param string $desiredSSOBinding Parse specific binding SSO endpoint + * @param string $desiredSLOBinding Parse specific binding SLO endpoint + * + * @return array metadata info in php-saml settings format + * + * @throws Exception + */ + public static function parseXML($xml, $entityId = null, $desiredNameIdFormat = null, $desiredSSOBinding = Constants::BINDING_HTTP_REDIRECT, $desiredSLOBinding = Constants::BINDING_HTTP_REDIRECT) + { + $metadataInfo = array(); + + $dom = new DOMDocument(); + $dom->preserveWhiteSpace = false; + $dom->formatOutput = true; + try { + $dom = Utils::loadXML($dom, $xml); + if (!$dom) { + throw new Exception('Error parsing metadata'); + } + + $customIdPStr = ''; + if (!empty($entityId)) { + $customIdPStr = '[@entityID="' . $entityId . '"]'; + } + $idpDescryptorXPath = '//md:EntityDescriptor' . $customIdPStr . '/md:IDPSSODescriptor'; + + $idpDescriptorNodes = Utils::query($dom, $idpDescryptorXPath); + + if (isset($idpDescriptorNodes) && $idpDescriptorNodes->length > 0) { + $metadataInfo['idp'] = array(); + + $idpDescriptor = $idpDescriptorNodes->item(0); + + if (empty($entityId) && $idpDescriptor->parentNode->hasAttribute('entityID')) { + $entityId = $idpDescriptor->parentNode->getAttribute('entityID'); + } + + if (!empty($entityId)) { + $metadataInfo['idp']['entityId'] = $entityId; + } + + $ssoNodes = Utils::query($dom, './md:SingleSignOnService[@Binding="'.$desiredSSOBinding.'"]', $idpDescriptor); + if ($ssoNodes->length < 1) { + $ssoNodes = Utils::query($dom, './md:SingleSignOnService', $idpDescriptor); + } + if ($ssoNodes->length > 0) { + $metadataInfo['idp']['singleSignOnService'] = array( + 'url' => $ssoNodes->item(0)->getAttribute('Location'), + 'binding' => $ssoNodes->item(0)->getAttribute('Binding') + ); + } + + $sloNodes = Utils::query($dom, './md:SingleLogoutService[@Binding="'.$desiredSLOBinding.'"]', $idpDescriptor); + if ($sloNodes->length < 1) { + $sloNodes = Utils::query($dom, './md:SingleLogoutService', $idpDescriptor); + } + if ($sloNodes->length > 0) { + $metadataInfo['idp']['singleLogoutService'] = array( + 'url' => $sloNodes->item(0)->getAttribute('Location'), + 'responseUrl' => $sloNodes->item(0)->getAttribute('ResponseLocation'), + 'binding' => $sloNodes->item(0)->getAttribute('Binding') + ); + } + + $keyDescriptorCertSigningNodes = Utils::query($dom, './md:KeyDescriptor[not(contains(@use, "encryption"))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate', $idpDescriptor); + + $keyDescriptorCertEncryptionNodes = Utils::query($dom, './md:KeyDescriptor[not(contains(@use, "signing"))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate', $idpDescriptor); + + if (!empty($keyDescriptorCertSigningNodes) || !empty($keyDescriptorCertEncryptionNodes)) { + $metadataInfo['idp']['x509certMulti'] = array(); + if (!empty($keyDescriptorCertSigningNodes)) { + $idpInfo['x509certMulti']['signing'] = array(); + foreach ($keyDescriptorCertSigningNodes as $keyDescriptorCertSigningNode) { + $metadataInfo['idp']['x509certMulti']['signing'][] = Utils::formatCert($keyDescriptorCertSigningNode->nodeValue, false); + } + } + if (!empty($keyDescriptorCertEncryptionNodes)) { + $idpInfo['x509certMulti']['encryption'] = array(); + foreach ($keyDescriptorCertEncryptionNodes as $keyDescriptorCertEncryptionNode) { + $metadataInfo['idp']['x509certMulti']['encryption'][] = Utils::formatCert($keyDescriptorCertEncryptionNode->nodeValue, false); + } + } + + $idpCertdata = $metadataInfo['idp']['x509certMulti']; + if ((count($idpCertdata) == 1 and + ((isset($idpCertdata['signing']) and count($idpCertdata['signing']) == 1) or (isset($idpCertdata['encryption']) and count($idpCertdata['encryption']) == 1))) or + ((isset($idpCertdata['signing']) && count($idpCertdata['signing']) == 1) && isset($idpCertdata['encryption']) && count($idpCertdata['encryption']) == 1 && strcmp($idpCertdata['signing'][0], $idpCertdata['encryption'][0]) == 0)) { + if (isset($metadataInfo['idp']['x509certMulti']['signing'][0])) { + $metadataInfo['idp']['x509cert'] = $metadataInfo['idp']['x509certMulti']['signing'][0]; + } else { + $metadataInfo['idp']['x509cert'] = $metadataInfo['idp']['x509certMulti']['encryption'][0]; + } + unset($metadataInfo['idp']['x509certMulti']); + } + } + + $nameIdFormatNodes = Utils::query($dom, './md:NameIDFormat', $idpDescriptor); + if ($nameIdFormatNodes->length > 0) { + $metadataInfo['sp']['NameIDFormat'] = $nameIdFormatNodes->item(0)->nodeValue; + if (!empty($desiredNameIdFormat)) { + foreach ($nameIdFormatNodes as $nameIdFormatNode) { + if (strcmp($nameIdFormatNode->nodeValue, $desiredNameIdFormat) == 0) { + $metadataInfo['sp']['NameIDFormat'] = $nameIdFormatNode->nodeValue; + break; + } + } + } + } + } + } catch (Exception $e) { + throw new Exception('Error parsing metadata. '.$e->getMessage()); + } + + return $metadataInfo; + } + + /** + * Inject metadata info into php-saml settings array + * + * @param array $settings php-saml settings array + * @param array $metadataInfo array metadata info + * + * @return array settings + */ + public static function injectIntoSettings($settings, $metadataInfo) + { + if (isset($metadataInfo['idp']) && isset($settings['idp'])) { + if (isset($metadataInfo['idp']['x509certMulti']) && !empty($metadataInfo['idp']['x509certMulti']) && isset($settings['idp']['x509cert'])) { + unset($settings['idp']['x509cert']); + } + + if (isset($metadataInfo['idp']['x509cert']) && !empty($metadataInfo['idp']['x509cert']) && isset($settings['idp']['x509certMulti'])) { + unset($settings['idp']['x509certMulti']); + } + } + + return array_replace_recursive($settings, $metadataInfo); + } +} diff --git a/vendor/onelogin/php-saml/src/Saml2/LogoutRequest.php b/vendor/onelogin/php-saml/src/Saml2/LogoutRequest.php new file mode 100644 index 0000000000..4cd7efb020 --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/LogoutRequest.php @@ -0,0 +1,478 @@ + + * @license MIT https://github.com/onelogin/php-saml/blob/master/LICENSE + * @link https://github.com/onelogin/php-saml + */ + +namespace OneLogin\Saml2; + +use RobRichards\XMLSecLibs\XMLSecurityKey; + +use DOMDocument; +use Exception; + +/** + * SAML 2 Logout Request + */ +class LogoutRequest +{ + /** + * Contains the ID of the Logout Request + * + * @var string + */ + public $id; + + /** + * Object that represents the setting info + * + * @var Settings + */ + protected $_settings; + + /** + * SAML Logout Request + * + * @var string + */ + protected $_logoutRequest; + + /** + * After execute a validation process, this var contains the cause + * + * @var Exception + */ + private $_error; + + /** + * Constructs the Logout Request object. + * + * @param Settings $settings Settings + * @param string|null $request A UUEncoded Logout Request. + * @param string|null $nameId The NameID that will be set in the LogoutRequest. + * @param string|null $sessionIndex The SessionIndex (taken from the SAML Response in the SSO process). + * @param string|null $nameIdFormat The NameID Format will be set in the LogoutRequest. + * @param string|null $nameIdNameQualifier The NameID NameQualifier will be set in the LogoutRequest. + * @param string|null $nameIdSPNameQualifier The NameID SP NameQualifier will be set in the LogoutRequest. + */ + public function __construct(\OneLogin\Saml2\Settings $settings, $request = null, $nameId = null, $sessionIndex = null, $nameIdFormat = null, $nameIdNameQualifier = null, $nameIdSPNameQualifier = null) + { + $this->_settings = $settings; + + $baseURL = $this->_settings->getBaseURL(); + if (!empty($baseURL)) { + Utils::setBaseURL($baseURL); + } + + if (!isset($request) || empty($request)) { + $spData = $this->_settings->getSPData(); + $idpData = $this->_settings->getIdPData(); + $security = $this->_settings->getSecurityData(); + + $id = Utils::generateUniqueID(); + $this->id = $id; + + $issueInstant = Utils::parseTime2SAML(time()); + + $cert = null; + if (isset($security['nameIdEncrypted']) && $security['nameIdEncrypted']) { + $existsMultiX509Enc = isset($idpData['x509certMulti']) && isset($idpData['x509certMulti']['encryption']) && !empty($idpData['x509certMulti']['encryption']); + + if ($existsMultiX509Enc) { + $cert = $idpData['x509certMulti']['encryption'][0]; + } else { + $cert = $idpData['x509cert']; + } + } + + if (!empty($nameId)) { + if (empty($nameIdFormat) + && $spData['NameIDFormat'] != Constants::NAMEID_UNSPECIFIED) { + $nameIdFormat = $spData['NameIDFormat']; + } + } else { + $nameId = $idpData['entityId']; + $nameIdFormat = Constants::NAMEID_ENTITY; + } + + /* From saml-core-2.0-os 8.3.6, when the entity Format is used: + "The NameQualifier, SPNameQualifier, and SPProvidedID attributes MUST be omitted. + */ + if (!empty($nameIdFormat) && $nameIdFormat == Constants::NAMEID_ENTITY) { + $nameIdNameQualifier = null; + $nameIdSPNameQualifier = null; + } + + // NameID Format UNSPECIFIED omitted + if (!empty($nameIdFormat) && $nameIdFormat == Constants::NAMEID_UNSPECIFIED) { + $nameIdFormat = null; + } + + $nameIdObj = Utils::generateNameId( + $nameId, + $nameIdSPNameQualifier, + $nameIdFormat, + $cert, + $nameIdNameQualifier + ); + + $sessionIndexStr = isset($sessionIndex) ? "{$sessionIndex}" : ""; + + $spEntityId = htmlspecialchars($spData['entityId'], ENT_QUOTES); + $logoutRequest = << + {$spEntityId} + {$nameIdObj} + {$sessionIndexStr} + +LOGOUTREQUEST; + } else { + $decoded = base64_decode($request); + // We try to inflate + $inflated = @gzinflate($decoded); + if ($inflated != false) { + $logoutRequest = $inflated; + } else { + $logoutRequest = $decoded; + } + $this->id = static::getID($logoutRequest); + } + $this->_logoutRequest = $logoutRequest; + } + + /** + * Returns the Logout Request defated, base64encoded, unsigned + * + * @param bool|null $deflate Whether or not we should 'gzdeflate' the request body before we return it. + * + * @return string Deflated base64 encoded Logout Request + */ + public function getRequest($deflate = null) + { + $subject = $this->_logoutRequest; + + if (is_null($deflate)) { + $deflate = $this->_settings->shouldCompressRequests(); + } + + if ($deflate) { + $subject = gzdeflate($this->_logoutRequest); + } + + return base64_encode($subject); + } + + /** + * Returns the ID of the Logout Request. + * + * @param string|DOMDocument $request Logout Request Message + * + * @return string ID + * + * @throws Error + */ + public static function getID($request) + { + if ($request instanceof DOMDocument) { + $dom = $request; + } else { + $dom = new DOMDocument(); + $dom = Utils::loadXML($dom, $request); + } + + + if (false === $dom) { + throw new Error( + "LogoutRequest could not be processed", + Error::SAML_LOGOUTREQUEST_INVALID + ); + } + + $id = $dom->documentElement->getAttribute('ID'); + return $id; + } + + /** + * Gets the NameID Data of the the Logout Request. + * + * @param string|DOMDocument $request Logout Request Message + * @param string|null $key The SP key + * + * @return array Name ID Data (Value, Format, NameQualifier, SPNameQualifier) + * + * @throws Error + * @throws Exception + * @throws ValidationError + */ + public static function getNameIdData($request, $key = null) + { + if ($request instanceof DOMDocument) { + $dom = $request; + } else { + $dom = new DOMDocument(); + $dom = Utils::loadXML($dom, $request); + } + + $encryptedEntries = Utils::query($dom, '/samlp:LogoutRequest/saml:EncryptedID'); + + if ($encryptedEntries->length == 1) { + $encryptedDataNodes = $encryptedEntries->item(0)->getElementsByTagName('EncryptedData'); + $encryptedData = $encryptedDataNodes->item(0); + + if (empty($key)) { + throw new Error( + "Private Key is required in order to decrypt the NameID, check settings", + Error::PRIVATE_KEY_NOT_FOUND + ); + } + + $seckey = new XMLSecurityKey(XMLSecurityKey::RSA_1_5, array('type'=>'private')); + $seckey->loadKey($key); + + $nameId = Utils::decryptElement($encryptedData, $seckey); + + } else { + $entries = Utils::query($dom, '/samlp:LogoutRequest/saml:NameID'); + if ($entries->length == 1) { + $nameId = $entries->item(0); + } + } + + if (!isset($nameId)) { + throw new ValidationError( + "NameID not found in the Logout Request", + ValidationError::NO_NAMEID + ); + } + + $nameIdData = array(); + $nameIdData['Value'] = $nameId->nodeValue; + foreach (array('Format', 'SPNameQualifier', 'NameQualifier') as $attr) { + if ($nameId->hasAttribute($attr)) { + $nameIdData[$attr] = $nameId->getAttribute($attr); + } + } + + return $nameIdData; + } + + /** + * Gets the NameID of the Logout Request. + * + * @param string|DOMDocument $request Logout Request Message + * @param string|null $key The SP key + * + * @return string Name ID Value + * + * @throws Error + * @throws Exception + * @throws ValidationError + */ + public static function getNameId($request, $key = null) + { + $nameId = self::getNameIdData($request, $key); + return $nameId['Value']; + } + + /** + * Gets the Issuer of the Logout Request. + * + * @param string|DOMDocument $request Logout Request Message + * + * @return string|null $issuer The Issuer + * + * @throws Exception + */ + public static function getIssuer($request) + { + if ($request instanceof DOMDocument) { + $dom = $request; + } else { + $dom = new DOMDocument(); + $dom = Utils::loadXML($dom, $request); + } + + $issuer = null; + $issuerNodes = Utils::query($dom, '/samlp:LogoutRequest/saml:Issuer'); + if ($issuerNodes->length == 1) { + $issuer = $issuerNodes->item(0)->textContent; + } + return $issuer; + } + + /** + * Gets the SessionIndexes from the Logout Request. + * Notice: Our Constructor only support 1 SessionIndex but this parser + * extracts an array of all the SessionIndex found on a + * Logout Request, that could be many. + * + * @param string|DOMDocument $request Logout Request Message + * + * @return array The SessionIndex value + * + * @throws Exception + */ + public static function getSessionIndexes($request) + { + if ($request instanceof DOMDocument) { + $dom = $request; + } else { + $dom = new DOMDocument(); + $dom = Utils::loadXML($dom, $request); + } + + $sessionIndexes = array(); + $sessionIndexNodes = Utils::query($dom, '/samlp:LogoutRequest/samlp:SessionIndex'); + foreach ($sessionIndexNodes as $sessionIndexNode) { + $sessionIndexes[] = $sessionIndexNode->textContent; + } + return $sessionIndexes; + } + + /** + * Checks if the Logout Request recieved is valid. + * + * @param bool $retrieveParametersFromServer True if we want to use parameters from $_SERVER to validate the signature + * + * @return bool If the Logout Request is or not valid + * + * @throws Exception + * @throws ValidationError + */ + public function isValid($retrieveParametersFromServer = false) + { + $this->_error = null; + try { + $dom = new DOMDocument(); + $dom = Utils::loadXML($dom, $this->_logoutRequest); + + $idpData = $this->_settings->getIdPData(); + $idPEntityId = $idpData['entityId']; + + if ($this->_settings->isStrict()) { + $security = $this->_settings->getSecurityData(); + + if ($security['wantXMLValidation']) { + $res = Utils::validateXML($dom, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive()); + if (!$res instanceof DOMDocument) { + throw new ValidationError( + "Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd", + ValidationError::INVALID_XML_FORMAT + ); + } + } + + $currentURL = Utils::getSelfRoutedURLNoQuery(); + + // Check NotOnOrAfter + if ($dom->documentElement->hasAttribute('NotOnOrAfter')) { + $na = Utils::parseSAML2Time($dom->documentElement->getAttribute('NotOnOrAfter')); + if ($na <= time()) { + throw new ValidationError( + "Could not validate timestamp: expired. Check system clock.", + ValidationError::RESPONSE_EXPIRED + ); + } + } + + // Check destination + if ($dom->documentElement->hasAttribute('Destination')) { + $destination = $dom->documentElement->getAttribute('Destination'); + if (!empty($destination) && strpos($destination, $currentURL) === false) { + throw new ValidationError( + "The LogoutRequest was received at $currentURL instead of $destination", + ValidationError::WRONG_DESTINATION + ); + } + } + + $nameId = static::getNameId($dom, $this->_settings->getSPkey()); + + // Check issuer + $issuer = static::getIssuer($dom); + if (!empty($issuer) && $issuer != $idPEntityId) { + throw new ValidationError( + "Invalid issuer in the Logout Request", + ValidationError::WRONG_ISSUER + ); + } + + if ($security['wantMessagesSigned'] && !isset($_GET['Signature'])) { + throw new ValidationError( + "The Message of the Logout Request is not signed and the SP require it", + ValidationError::NO_SIGNED_MESSAGE + ); + } + } + + if (isset($_GET['Signature'])) { + $signatureValid = Utils::validateBinarySign("SAMLRequest", $_GET, $idpData, $retrieveParametersFromServer); + if (!$signatureValid) { + throw new ValidationError( + "Signature validation failed. Logout Request rejected", + ValidationError::INVALID_SIGNATURE + ); + } + } + + return true; + } catch (Exception $e) { + $this->_error = $e; + $debug = $this->_settings->isDebugActive(); + if ($debug) { + echo htmlentities($this->_error->getMessage()); + } + return false; + } + } + + /** + * After execute a validation process, if fails this method returns the Exception of the cause + * + * @return Exception Cause + */ + public function getErrorException() + { + return $this->_error; + } + + /** + * After execute a validation process, if fails this method returns the cause + * + * @return null|string Error reason + */ + public function getError() + { + $errorMsg = null; + if (isset($this->_error)) { + $errorMsg = htmlentities($this->_error->getMessage()); + } + return $errorMsg; + } + + /** + * Returns the XML that will be sent as part of the request + * or that was received at the SP + * + * @return string + */ + public function getXML() + { + return $this->_logoutRequest; + } +} diff --git a/vendor/onelogin/php-saml/src/Saml2/LogoutResponse.php b/vendor/onelogin/php-saml/src/Saml2/LogoutResponse.php new file mode 100644 index 0000000000..2f376bb5d9 --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/LogoutResponse.php @@ -0,0 +1,336 @@ + + * @license MIT https://github.com/onelogin/php-saml/blob/master/LICENSE + * @link https://github.com/onelogin/php-saml + */ + +namespace OneLogin\Saml2; + +use DOMDocument; +use DOMNodeList; +use Exception; + +/** + * SAML 2 Logout Response + */ +class LogoutResponse +{ + /** + * Contains the ID of the Logout Response + * + * @var string + */ + public $id; + + /** + * Object that represents the setting info + * + * @var Settings + */ + protected $_settings; + + /** + * The decoded, unprocessed XML response provided to the constructor. + * + * @var string|null + */ + protected $_logoutResponse; + + /** + * A DOMDocument class loaded from the SAML LogoutResponse. + * + * @var DOMDocument + */ + public $document; + + /** + * After execute a validation process, if it fails, this var contains the cause + * + * @var Exception|null + */ + private $_error; + + /** + * Constructs a Logout Response object (Initialize params from settings and if provided + * load the Logout Response. + * + * @param Settings $settings Settings. + * @param string|null $response An UUEncoded SAML Logout response from the IdP. + * + * @throws Error + * @throws Exception + * + */ + public function __construct(\OneLogin\Saml2\Settings $settings, $response = null) + { + $this->_settings = $settings; + + $baseURL = $this->_settings->getBaseURL(); + if (!empty($baseURL)) { + Utils::setBaseURL($baseURL); + } + + if ($response) { + $decoded = base64_decode($response); + $inflated = @gzinflate($decoded); + if ($inflated != false) { + $this->_logoutResponse = $inflated; + } else { + $this->_logoutResponse = $decoded; + } + $this->document = new DOMDocument(); + $this->document = Utils::loadXML($this->document, $this->_logoutResponse); + + if (false === $this->document) { + throw new Error( + "LogoutResponse could not be processed", + Error::SAML_LOGOUTRESPONSE_INVALID + ); + } + + if ($this->document->documentElement->hasAttribute('ID')) { + $this->id = $this->document->documentElement->getAttribute('ID'); + } + } + } + + /** + * Gets the Issuer of the Logout Response. + * + * @return string|null $issuer The Issuer + */ + public function getIssuer() + { + $issuer = null; + $issuerNodes = $this->_query('/samlp:LogoutResponse/saml:Issuer'); + if ($issuerNodes->length == 1) { + $issuer = $issuerNodes->item(0)->textContent; + } + return $issuer; + } + + /** + * Gets the Status of the Logout Response. + * + * @return string|null The Status + */ + public function getStatus() + { + $entries = $this->_query('/samlp:LogoutResponse/samlp:Status/samlp:StatusCode'); + if ($entries->length != 1) { + return null; + } + $status = $entries->item(0)->getAttribute('Value'); + return $status; + } + + /** + * Determines if the SAML LogoutResponse is valid + * + * @param string|null $requestId The ID of the LogoutRequest sent by this SP to the IdP + * @param bool $retrieveParametersFromServer True if we want to use parameters from $_SERVER to validate the signature + * + * @return bool Returns if the SAML LogoutResponse is or not valid + * + * @throws ValidationError + */ + public function isValid($requestId = null, $retrieveParametersFromServer = false) + { + $this->_error = null; + try { + $idpData = $this->_settings->getIdPData(); + $idPEntityId = $idpData['entityId']; + + if ($this->_settings->isStrict()) { + $security = $this->_settings->getSecurityData(); + + if ($security['wantXMLValidation']) { + $res = Utils::validateXML($this->document, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive()); + if (!$res instanceof DOMDocument) { + throw new ValidationError( + "Invalid SAML Logout Response. Not match the saml-schema-protocol-2.0.xsd", + ValidationError::INVALID_XML_FORMAT + ); + } + } + + // Check if the InResponseTo of the Logout Response matchs the ID of the Logout Request (requestId) if provided + if (isset($requestId) && $this->document->documentElement->hasAttribute('InResponseTo')) { + $inResponseTo = $this->document->documentElement->getAttribute('InResponseTo'); + if ($requestId != $inResponseTo) { + throw new ValidationError( + "The InResponseTo of the Logout Response: $inResponseTo, does not match the ID of the Logout request sent by the SP: $requestId", + ValidationError::WRONG_INRESPONSETO + ); + } + } + + // Check issuer + $issuer = $this->getIssuer(); + if (!empty($issuer) && $issuer != $idPEntityId) { + throw new ValidationError( + "Invalid issuer in the Logout Response", + ValidationError::WRONG_ISSUER + ); + } + + $currentURL = Utils::getSelfRoutedURLNoQuery(); + + // Check destination + if ($this->document->documentElement->hasAttribute('Destination')) { + $destination = $this->document->documentElement->getAttribute('Destination'); + if (!empty($destination) && strpos($destination, $currentURL) === false) { + throw new ValidationError( + "The LogoutResponse was received at $currentURL instead of $destination", + ValidationError::WRONG_DESTINATION + ); + } + } + + if ($security['wantMessagesSigned'] && !isset($_GET['Signature'])) { + throw new ValidationError( + "The Message of the Logout Response is not signed and the SP requires it", + ValidationError::NO_SIGNED_MESSAGE + ); + } + } + + if (isset($_GET['Signature'])) { + $signatureValid = Utils::validateBinarySign("SAMLResponse", $_GET, $idpData, $retrieveParametersFromServer); + if (!$signatureValid) { + throw new ValidationError( + "Signature validation failed. Logout Response rejected", + ValidationError::INVALID_SIGNATURE + ); + } + } + return true; + } catch (Exception $e) { + $this->_error = $e; + $debug = $this->_settings->isDebugActive(); + if ($debug) { + echo htmlentities($this->_error->getMessage()); + } + return false; + } + } + + /** + * Extracts a node from the DOMDocument (Logout Response Menssage) + * + * @param string $query Xpath Expression + * + * @return DOMNodeList The queried node + */ + private function _query($query) + { + return Utils::query($this->document, $query); + + } + + /** + * Generates a Logout Response object. + * + * @param string $inResponseTo InResponseTo value for the Logout Response. + */ + public function build($inResponseTo) + { + + $spData = $this->_settings->getSPData(); + $idpData = $this->_settings->getIdPData(); + + $this->id = Utils::generateUniqueID(); + $issueInstant = Utils::parseTime2SAML(time()); + + $spEntityId = htmlspecialchars($spData['entityId'], ENT_QUOTES); + $logoutResponse = << + {$spEntityId} + + + + +LOGOUTRESPONSE; + $this->_logoutResponse = $logoutResponse; + } + + /** + * Returns a Logout Response object. + * + * @param bool|null $deflate Whether or not we should 'gzdeflate' the response body before we return it. + * + * @return string Logout Response deflated and base64 encoded + */ + public function getResponse($deflate = null) + { + $logoutResponse = $this->_logoutResponse; + + if (is_null($deflate)) { + $deflate = $this->_settings->shouldCompressResponses(); + } + + if ($deflate) { + $logoutResponse = gzdeflate($this->_logoutResponse); + } + return base64_encode($logoutResponse); + } + + /** + * After execute a validation process, if fails this method returns the cause. + * + * @return Exception|null Cause + */ + public function getErrorException() + { + return $this->_error; + } + + /** + * After execute a validation process, if fails this method returns the cause + * + * @return null|string Error reason + */ + public function getError() + { + $errorMsg = null; + if (isset($this->_error)) { + $errorMsg = htmlentities($this->_error->getMessage()); + } + return $errorMsg; + } + + /** + * @return string the ID of the Response + */ + public function getId() + { + return $this->id; + } + + /** + * Returns the XML that will be sent as part of the response + * or that was received at the SP + * + * @return string|null + */ + public function getXML() + { + return $this->_logoutResponse; + } +} diff --git a/vendor/onelogin/php-saml/src/Saml2/Metadata.php b/vendor/onelogin/php-saml/src/Saml2/Metadata.php new file mode 100644 index 0000000000..922ad60baa --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/Metadata.php @@ -0,0 +1,267 @@ + + * @license MIT https://github.com/onelogin/php-saml/blob/master/LICENSE + * @link https://github.com/onelogin/php-saml + */ + +namespace OneLogin\Saml2; + +use RobRichards\XMLSecLibs\XMLSecurityKey; +use RobRichards\XMLSecLibs\XMLSecurityDSig; + +use DOMDocument; +use Exception; + +/** + * Metadata lib of OneLogin PHP Toolkit + */ +class Metadata +{ + const TIME_VALID = 172800; // 2 days + const TIME_CACHED = 604800; // 1 week + + /** + * Generates the metadata of the SP based on the settings + * + * @param array $sp The SP data + * @param bool|string $authnsign authnRequestsSigned attribute + * @param bool|string $wsign wantAssertionsSigned attribute + * @param int|null $validUntil Metadata's valid time + * @param int|null $cacheDuration Duration of the cache in seconds + * @param array $contacts Contacts info + * @param array $organization Organization ingo + * @param array $attributes + * + * @return string SAML Metadata XML + */ + public static function builder($sp, $authnsign = false, $wsign = false, $validUntil = null, $cacheDuration = null, $contacts = array(), $organization = array(), $attributes = array()) + { + + if (!isset($validUntil)) { + $validUntil = time() + self::TIME_VALID; + } + $validUntilTime = Utils::parseTime2SAML($validUntil); + + if (!isset($cacheDuration)) { + $cacheDuration = self::TIME_CACHED; + } + + $sls = ''; + + if (isset($sp['singleLogoutService'])) { + $slsUrl = htmlspecialchars($sp['singleLogoutService']['url'], ENT_QUOTES); + $sls = << + +SLS_TEMPLATE; + } + + if ($authnsign) { + $strAuthnsign = 'true'; + } else { + $strAuthnsign = 'false'; + } + + if ($wsign) { + $strWsign = 'true'; + } else { + $strWsign = 'false'; + } + + $strOrganization = ''; + + if (!empty($organization)) { + $organizationInfoNames = array(); + $organizationInfoDisplaynames = array(); + $organizationInfoUrls = array(); + foreach ($organization as $lang => $info) { + $organizationInfoNames[] = <<{$info['name']} +ORGANIZATION_NAME; + $organizationInfoDisplaynames[] = <<{$info['displayname']} +ORGANIZATION_DISPLAY; + $organizationInfoUrls[] = <<{$info['url']} +ORGANIZATION_URL; + } + $orgData = implode("\n", $organizationInfoNames)."\n".implode("\n", $organizationInfoDisplaynames)."\n".implode("\n", $organizationInfoUrls); + $strOrganization = << +{$orgData} + +ORGANIZATIONSTR; + } + + $strContacts = ''; + if (!empty($contacts)) { + $contactsInfo = array(); + foreach ($contacts as $type => $info) { + $contactsInfo[] = << + {$info['givenName']} + {$info['emailAddress']} + +CONTACT; + } + $strContacts = "\n".implode("\n", $contactsInfo); + } + + $strAttributeConsumingService = ''; + if (isset($sp['attributeConsumingService'])) { + $attrCsDesc = ''; + if (isset($sp['attributeConsumingService']['serviceDescription'])) { + $attrCsDesc = sprintf( + ' %s' . PHP_EOL, + $sp['attributeConsumingService']['serviceDescription'] + ); + } + if (!isset($sp['attributeConsumingService']['serviceName'])) { + $sp['attributeConsumingService']['serviceName'] = 'Service'; + } + $requestedAttributeData = array(); + foreach ($sp['attributeConsumingService']['requestedAttributes'] as $attribute) { + $requestedAttributeStr = sprintf(' {$attrValue} +ATTRIBUTEVALUE; + } + $reqAttrAuxStr .= "\n "; + } + + $requestedAttributeData[] = $requestedAttributeStr . $reqAttrAuxStr; + } + + $requestedAttributeStr = implode(PHP_EOL, $requestedAttributeData); + $strAttributeConsumingService = << + {$sp['attributeConsumingService']['serviceName']} +{$attrCsDesc}{$requestedAttributeStr} + +METADATA_TEMPLATE; + } + + $spEntityId = htmlspecialchars($sp['entityId'], ENT_QUOTES); + $acsUrl = htmlspecialchars($sp['assertionConsumerService']['url'], ENT_QUOTES); + $metadata = << + + +{$sls} {$sp['NameIDFormat']} + + {$strAttributeConsumingService} + {$strOrganization}{$strContacts} + +METADATA_TEMPLATE; + return $metadata; + } + + /** + * Signs the metadata with the key/cert provided + * + * @param string $metadata SAML Metadata XML + * @param string $key x509 key + * @param string $cert x509 cert + * @param string $signAlgorithm Signature algorithm method + * @param string $digestAlgorithm Digest algorithm method + * + * @return string Signed Metadata + * + * @throws Exception + */ + public static function signMetadata($metadata, $key, $cert, $signAlgorithm = XMLSecurityKey::RSA_SHA256, $digestAlgorithm = XMLSecurityDSig::SHA256) + { + return Utils::addSign($metadata, $key, $cert, $signAlgorithm, $digestAlgorithm); + } + + /** + * Adds the x509 descriptors (sign/encryption) to the metadata + * The same cert will be used for sign/encrypt + * + * @param string $metadata SAML Metadata XML + * @param string $cert x509 cert + * @param bool $wantsEncrypted Whether to include the KeyDescriptor for encryption + * + * @return string Metadata with KeyDescriptors + * + * @throws Exception + */ + public static function addX509KeyDescriptors($metadata, $cert, $wantsEncrypted = true) + { + $xml = new DOMDocument(); + $xml->preserveWhiteSpace = false; + $xml->formatOutput = true; + try { + $xml = Utils::loadXML($xml, $metadata); + if (!$xml) { + throw new Exception('Error parsing metadata'); + } + } catch (Exception $e) { + throw new Exception('Error parsing metadata. '.$e->getMessage()); + } + + $formatedCert = Utils::formatCert($cert, false); + $x509Certificate = $xml->createElementNS(Constants::NS_DS, 'X509Certificate', $formatedCert); + + $keyData = $xml->createElementNS(Constants::NS_DS, 'ds:X509Data'); + $keyData->appendChild($x509Certificate); + + $keyInfo = $xml->createElementNS(Constants::NS_DS, 'ds:KeyInfo'); + $keyInfo->appendChild($keyData); + + $keyDescriptor = $xml->createElementNS(Constants::NS_MD, "md:KeyDescriptor"); + + $SPSSODescriptor = $xml->getElementsByTagName('SPSSODescriptor')->item(0); + $SPSSODescriptor->insertBefore($keyDescriptor->cloneNode(), $SPSSODescriptor->firstChild); + if ($wantsEncrypted === true) { + $SPSSODescriptor->insertBefore($keyDescriptor->cloneNode(), $SPSSODescriptor->firstChild); + } + + $signing = $xml->getElementsByTagName('KeyDescriptor')->item(0); + $signing->setAttribute('use', 'signing'); + $signing->appendChild($keyInfo); + + if ($wantsEncrypted === true) { + $encryption = $xml->getElementsByTagName('KeyDescriptor')->item(1); + $encryption->setAttribute('use', 'encryption'); + + $encryption->appendChild($keyInfo->cloneNode(true)); + } + + return $xml->saveXML(); + } +} diff --git a/vendor/onelogin/php-saml/src/Saml2/Response.php b/vendor/onelogin/php-saml/src/Saml2/Response.php new file mode 100644 index 0000000000..90c3d5cc1b --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/Response.php @@ -0,0 +1,1211 @@ + + * @license MIT https://github.com/onelogin/php-saml/blob/master/LICENSE + * @link https://github.com/onelogin/php-saml + */ + +namespace OneLogin\Saml2; + +use RobRichards\XMLSecLibs\XMLSecurityKey; +use RobRichards\XMLSecLibs\XMLSecEnc; + +use DOMDocument; +use DOMNodeList; +use DOMXPath; +use Exception; + +/** + * SAML 2 Authentication Response + */ +class Response +{ + /** + * Settings + * + * @var Settings + */ + protected $_settings; + + /** + * The decoded, unprocessed XML response provided to the constructor. + * + * @var string + */ + public $response; + + /** + * A DOMDocument class loaded from the SAML Response. + * + * @var DOMDocument + */ + public $document; + + /** + * A DOMDocument class loaded from the SAML Response (Decrypted). + * + * @var DOMDocument + */ + public $decryptedDocument; + + /** + * The response contains an encrypted assertion. + * + * @var bool + */ + public $encrypted = false; + + /** + * After validation, if it fail this var has the cause of the problem + * + * @var Exception|null + */ + private $_error; + + /** + * NotOnOrAfter value of a valid SubjectConfirmationData node + * + * @var int + */ + private $_validSCDNotOnOrAfter; + + /** + * Constructs the SAML Response object. + * + * @param Settings $settings Settings. + * @param string $response A UUEncoded SAML response from the IdP. + * + * @throws Exception + * @throws ValidationError + */ + public function __construct(\OneLogin\Saml2\Settings $settings, $response) + { + $this->_settings = $settings; + + $baseURL = $this->_settings->getBaseURL(); + if (!empty($baseURL)) { + Utils::setBaseURL($baseURL); + } + + $this->response = base64_decode($response); + + $this->document = new DOMDocument(); + $this->document = Utils::loadXML($this->document, $this->response); + if (!$this->document) { + throw new ValidationError( + "SAML Response could not be processed", + ValidationError::INVALID_XML_FORMAT + ); + } + + // Quick check for the presence of EncryptedAssertion + $encryptedAssertionNodes = $this->document->getElementsByTagName('EncryptedAssertion'); + if ($encryptedAssertionNodes->length !== 0) { + $this->decryptedDocument = clone $this->document; + $this->encrypted = true; + $this->decryptedDocument = $this->decryptAssertion($this->decryptedDocument); + } + } + + /** + * Determines if the SAML Response is valid using the certificate. + * + * @param string|null $requestId The ID of the AuthNRequest sent by this SP to the IdP + * + * @return bool Validate the document + * + * @throws Exception + * @throws ValidationError + */ + public function isValid($requestId = null) + { + $this->_error = null; + try { + // Check SAML version + if ($this->document->documentElement->getAttribute('Version') != '2.0') { + throw new ValidationError( + "Unsupported SAML version", + ValidationError::UNSUPPORTED_SAML_VERSION + ); + } + + if (!$this->document->documentElement->hasAttribute('ID')) { + throw new ValidationError( + "Missing ID attribute on SAML Response", + ValidationError::MISSING_ID + ); + } + + $this->checkStatus(); + + $singleAssertion = $this->validateNumAssertions(); + if (!$singleAssertion) { + throw new ValidationError( + "SAML Response must contain 1 assertion", + ValidationError::WRONG_NUMBER_OF_ASSERTIONS + ); + } + + $idpData = $this->_settings->getIdPData(); + $idPEntityId = $idpData['entityId']; + $spData = $this->_settings->getSPData(); + $spEntityId = $spData['entityId']; + + $signedElements = $this->processSignedElements(); + + $responseTag = '{'.Constants::NS_SAMLP.'}Response'; + $assertionTag = '{'.Constants::NS_SAML.'}Assertion'; + + $hasSignedResponse = in_array($responseTag, $signedElements); + $hasSignedAssertion = in_array($assertionTag, $signedElements); + + if ($this->_settings->isStrict()) { + $security = $this->_settings->getSecurityData(); + + if ($security['wantXMLValidation']) { + $errorXmlMsg = "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"; + $res = Utils::validateXML($this->document, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive()); + if (!$res instanceof DOMDocument) { + throw new ValidationError( + $errorXmlMsg, + ValidationError::INVALID_XML_FORMAT + ); + } + + // If encrypted, check also the decrypted document + if ($this->encrypted) { + $res = Utils::validateXML($this->decryptedDocument, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive()); + if (!$res instanceof DOMDocument) { + throw new ValidationError( + $errorXmlMsg, + ValidationError::INVALID_XML_FORMAT + ); + } + } + + } + + $currentURL = Utils::getSelfRoutedURLNoQuery(); + + if ($this->document->documentElement->hasAttribute('InResponseTo')) { + $responseInResponseTo = $this->document->documentElement->getAttribute('InResponseTo'); + } + + // Check if the InResponseTo of the Response matchs the ID of the AuthNRequest (requestId) if provided + if (isset($requestId) && isset($responseInResponseTo) && $requestId != $responseInResponseTo) { + throw new ValidationError( + "The InResponseTo of the Response: $responseInResponseTo, does not match the ID of the AuthNRequest sent by the SP: $requestId", + ValidationError::WRONG_INRESPONSETO + ); + } + + if (!$this->encrypted && $security['wantAssertionsEncrypted']) { + throw new ValidationError( + "The assertion of the Response is not encrypted and the SP requires it", + ValidationError::NO_ENCRYPTED_ASSERTION + ); + } + + if ($security['wantNameIdEncrypted']) { + $encryptedIdNodes = $this->_queryAssertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData'); + if ($encryptedIdNodes->length != 1) { + throw new ValidationError( + "The NameID of the Response is not encrypted and the SP requires it", + ValidationError::NO_ENCRYPTED_NAMEID + ); + } + } + + // Validate Conditions element exists + if (!$this->checkOneCondition()) { + throw new ValidationError( + "The Assertion must include a Conditions element", + ValidationError::MISSING_CONDITIONS + ); + } + + // Validate Asserion timestamps + $this->validateTimestamps(); + + // Validate AuthnStatement element exists and is unique + if (!$this->checkOneAuthnStatement()) { + throw new ValidationError( + "The Assertion must include an AuthnStatement element", + ValidationError::WRONG_NUMBER_OF_AUTHSTATEMENTS + ); + } + + // EncryptedAttributes are not supported + $encryptedAttributeNodes = $this->_queryAssertion('/saml:AttributeStatement/saml:EncryptedAttribute'); + if ($encryptedAttributeNodes->length > 0) { + throw new ValidationError( + "There is an EncryptedAttribute in the Response and this SP not support them", + ValidationError::ENCRYPTED_ATTRIBUTES + ); + } + + // Check destination + if ($this->document->documentElement->hasAttribute('Destination')) { + $destination = trim($this->document->documentElement->getAttribute('Destination')); + if (empty($destination)) { + if (!$security['relaxDestinationValidation']) { + throw new ValidationError( + "The response has an empty Destination value", + ValidationError::EMPTY_DESTINATION + ); + } + } else { + if (strpos($destination, $currentURL) !== 0) { + $currentURLNoRouted = Utils::getSelfURLNoQuery(); + + if (strpos($destination, $currentURLNoRouted) !== 0) { + throw new ValidationError( + "The response was received at $currentURL instead of $destination", + ValidationError::WRONG_DESTINATION + ); + } + } + } + } + + // Check audience + $validAudiences = $this->getAudiences(); + if (!empty($validAudiences) && !in_array($spEntityId, $validAudiences, true)) { + throw new ValidationError( + sprintf( + "Invalid audience for this Response (expected '%s', got '%s')", + $spEntityId, + implode(',', $validAudiences) + ), + ValidationError::WRONG_AUDIENCE + ); + } + + // Check the issuers + $issuers = $this->getIssuers(); + foreach ($issuers as $issuer) { + $trimmedIssuer = trim($issuer); + if (empty($trimmedIssuer) || $trimmedIssuer !== $idPEntityId) { + throw new ValidationError( + "Invalid issuer in the Assertion/Response (expected '$idPEntityId', got '$trimmedIssuer')", + ValidationError::WRONG_ISSUER + ); + } + } + + // Check the session Expiration + $sessionExpiration = $this->getSessionNotOnOrAfter(); + if (!empty($sessionExpiration) && $sessionExpiration + Constants::ALLOWED_CLOCK_DRIFT <= time()) { + throw new ValidationError( + "The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response", + ValidationError::SESSION_EXPIRED + ); + } + + // Check the SubjectConfirmation, at least one SubjectConfirmation must be valid + $anySubjectConfirmation = false; + $subjectConfirmationNodes = $this->_queryAssertion('/saml:Subject/saml:SubjectConfirmation'); + foreach ($subjectConfirmationNodes as $scn) { + if ($scn->hasAttribute('Method') && $scn->getAttribute('Method') != Constants::CM_BEARER) { + continue; + } + $subjectConfirmationDataNodes = $scn->getElementsByTagName('SubjectConfirmationData'); + if ($subjectConfirmationDataNodes->length == 0) { + continue; + } else { + $scnData = $subjectConfirmationDataNodes->item(0); + if ($scnData->hasAttribute('InResponseTo')) { + $inResponseTo = $scnData->getAttribute('InResponseTo'); + if (isset($responseInResponseTo) && $responseInResponseTo != $inResponseTo) { + continue; + } + } + if ($scnData->hasAttribute('Recipient')) { + $recipient = $scnData->getAttribute('Recipient'); + if (!empty($recipient) && strpos($recipient, $currentURL) === false) { + continue; + } + } + if ($scnData->hasAttribute('NotOnOrAfter')) { + $noa = Utils::parseSAML2Time($scnData->getAttribute('NotOnOrAfter')); + if ($noa + Constants::ALLOWED_CLOCK_DRIFT <= time()) { + continue; + } + } + if ($scnData->hasAttribute('NotBefore')) { + $nb = Utils::parseSAML2Time($scnData->getAttribute('NotBefore')); + if ($nb > time() + Constants::ALLOWED_CLOCK_DRIFT) { + continue; + } + } + + // Save NotOnOrAfter value + if ($scnData->hasAttribute('NotOnOrAfter')) { + $this->_validSCDNotOnOrAfter = $noa; + } + $anySubjectConfirmation = true; + break; + } + } + + if (!$anySubjectConfirmation) { + throw new ValidationError( + "A valid SubjectConfirmation was not found on this Response", + ValidationError::WRONG_SUBJECTCONFIRMATION + ); + } + + if ($security['wantAssertionsSigned'] && !$hasSignedAssertion) { + throw new ValidationError( + "The Assertion of the Response is not signed and the SP requires it", + ValidationError::NO_SIGNED_ASSERTION + ); + } + + if ($security['wantMessagesSigned'] && !$hasSignedResponse) { + throw new ValidationError( + "The Message of the Response is not signed and the SP requires it", + ValidationError::NO_SIGNED_MESSAGE + ); + } + } + + // Detect case not supported + if ($this->encrypted) { + $encryptedIDNodes = Utils::query($this->decryptedDocument, '/samlp:Response/saml:Assertion/saml:Subject/saml:EncryptedID'); + if ($encryptedIDNodes->length > 0) { + throw new ValidationError( + 'Unsigned SAML Response that contains a signed and encrypted Assertion with encrypted nameId is not supported.', + ValidationError::NOT_SUPPORTED + ); + } + } + + if (empty($signedElements) || (!$hasSignedResponse && !$hasSignedAssertion)) { + throw new ValidationError( + 'No Signature found. SAML Response rejected', + ValidationError::NO_SIGNATURE_FOUND + ); + } else { + $cert = $idpData['x509cert']; + $fingerprint = $idpData['certFingerprint']; + $fingerprintalg = $idpData['certFingerprintAlgorithm']; + + $multiCerts = null; + $existsMultiX509Sign = isset($idpData['x509certMulti']) && isset($idpData['x509certMulti']['signing']) && !empty($idpData['x509certMulti']['signing']); + + if ($existsMultiX509Sign) { + $multiCerts = $idpData['x509certMulti']['signing']; + } + + // If find a Signature on the Response, validates it checking the original response + if ($hasSignedResponse && !Utils::validateSign($this->document, $cert, $fingerprint, $fingerprintalg, Utils::RESPONSE_SIGNATURE_XPATH, $multiCerts)) { + throw new ValidationError( + "Signature validation failed. SAML Response rejected", + ValidationError::INVALID_SIGNATURE + ); + } + + // If find a Signature on the Assertion (decrypted assertion if was encrypted) + $documentToCheckAssertion = $this->encrypted ? $this->decryptedDocument : $this->document; + if ($hasSignedAssertion && !Utils::validateSign($documentToCheckAssertion, $cert, $fingerprint, $fingerprintalg, Utils::ASSERTION_SIGNATURE_XPATH, $multiCerts)) { + throw new ValidationError( + "Signature validation failed. SAML Response rejected", + ValidationError::INVALID_SIGNATURE + ); + } + } + return true; + } catch (Exception $e) { + $this->_error = $e; + $debug = $this->_settings->isDebugActive(); + if ($debug) { + echo htmlentities($e->getMessage()); + } + return false; + } + } + + /** + * @return string|null the ID of the Response + */ + public function getId() + { + $id = null; + if ($this->document->documentElement->hasAttribute('ID')) { + $id = $this->document->documentElement->getAttribute('ID'); + } + return $id; + } + + /** + * @return string|null the ID of the assertion in the Response + * + * @throws ValidationError + */ + public function getAssertionId() + { + if (!$this->validateNumAssertions()) { + throw new ValidationError("SAML Response must contain 1 Assertion.", ValidationError::WRONG_NUMBER_OF_ASSERTIONS); + } + $assertionNodes = $this->_queryAssertion(""); + $id = null; + if ($assertionNodes->length == 1 && $assertionNodes->item(0)->hasAttribute('ID')) { + $id = $assertionNodes->item(0)->getAttribute('ID'); + } + return $id; + } + + /** + * @return int the NotOnOrAfter value of the valid SubjectConfirmationData + * node if any + */ + public function getAssertionNotOnOrAfter() + { + return $this->_validSCDNotOnOrAfter; + } + + /** + * Checks if the Status is success + * + * @throws ValidationError If status is not success + */ + public function checkStatus() + { + $status = Utils::getStatus($this->document); + + if (isset($status['code']) && $status['code'] !== Constants::STATUS_SUCCESS) { + $explodedCode = explode(':', $status['code']); + $printableCode = array_pop($explodedCode); + + $statusExceptionMsg = 'The status code of the Response was not Success, was '.$printableCode; + if (!empty($status['msg'])) { + $statusExceptionMsg .= ' -> '.$status['msg']; + } + throw new ValidationError( + $statusExceptionMsg, + ValidationError::STATUS_CODE_IS_NOT_SUCCESS + ); + } + } + + /** + * Checks that the samlp:Response/saml:Assertion/saml:Conditions element exists and is unique. + * + * @return boolean true if the Conditions element exists and is unique + */ + public function checkOneCondition() + { + $entries = $this->_queryAssertion("/saml:Conditions"); + if ($entries->length == 1) { + return true; + } else { + return false; + } + } + + /** + * Checks that the samlp:Response/saml:Assertion/saml:AuthnStatement element exists and is unique. + * + * @return boolean true if the AuthnStatement element exists and is unique + */ + public function checkOneAuthnStatement() + { + $entries = $this->_queryAssertion("/saml:AuthnStatement"); + if ($entries->length == 1) { + return true; + } else { + return false; + } + } + + /** + * Gets the audiences. + * + * @return array @audience The valid audiences of the response + */ + public function getAudiences() + { + $audiences = array(); + + $entries = $this->_queryAssertion('/saml:Conditions/saml:AudienceRestriction/saml:Audience'); + foreach ($entries as $entry) { + $value = trim($entry->textContent); + if (!empty($value)) { + $audiences[] = $value; + } + } + + return array_unique($audiences); + } + + /** + * Gets the Issuers (from Response and Assertion). + * + * @return array @issuers The issuers of the assertion/response + * + * @throws ValidationError + */ + public function getIssuers() + { + $issuers = array(); + + $responseIssuer = Utils::query($this->document, '/samlp:Response/saml:Issuer'); + if ($responseIssuer->length > 0) { + if ($responseIssuer->length == 1) { + $issuers[] = $responseIssuer->item(0)->textContent; + } else { + throw new ValidationError( + "Issuer of the Response is multiple.", + ValidationError::ISSUER_MULTIPLE_IN_RESPONSE + ); + } + } + + $assertionIssuer = $this->_queryAssertion('/saml:Issuer'); + if ($assertionIssuer->length == 1) { + $issuers[] = $assertionIssuer->item(0)->textContent; + } else { + throw new ValidationError( + "Issuer of the Assertion not found or multiple.", + ValidationError::ISSUER_NOT_FOUND_IN_ASSERTION + ); + } + + return array_unique($issuers); + } + + /** + * Gets the NameID Data provided by the SAML response from the IdP. + * + * @return array Name ID Data (Value, Format, NameQualifier, SPNameQualifier) + * + * @throws ValidationError + */ + public function getNameIdData() + { + $encryptedIdDataEntries = $this->_queryAssertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData'); + + if ($encryptedIdDataEntries->length == 1) { + $encryptedData = $encryptedIdDataEntries->item(0); + + $key = $this->_settings->getSPkey(); + $seckey = new XMLSecurityKey(XMLSecurityKey::RSA_1_5, array('type'=>'private')); + $seckey->loadKey($key); + + $nameId = Utils::decryptElement($encryptedData, $seckey); + + } else { + $entries = $this->_queryAssertion('/saml:Subject/saml:NameID'); + if ($entries->length == 1) { + $nameId = $entries->item(0); + } + } + + $nameIdData = array(); + + if (!isset($nameId)) { + $security = $this->_settings->getSecurityData(); + if ($security['wantNameId']) { + throw new ValidationError( + "NameID not found in the assertion of the Response", + ValidationError::NO_NAMEID + ); + } + } else { + if ($this->_settings->isStrict() && empty($nameId->nodeValue)) { + throw new ValidationError( + "An empty NameID value found", + ValidationError::EMPTY_NAMEID + ); + } + $nameIdData['Value'] = $nameId->nodeValue; + + foreach (array('Format', 'SPNameQualifier', 'NameQualifier') as $attr) { + if ($nameId->hasAttribute($attr)) { + if ($this->_settings->isStrict() && $attr == 'SPNameQualifier') { + $spData = $this->_settings->getSPData(); + $spEntityId = $spData['entityId']; + if ($spEntityId != $nameId->getAttribute($attr)) { + throw new ValidationError( + "The SPNameQualifier value mistmatch the SP entityID value.", + ValidationError::SP_NAME_QUALIFIER_NAME_MISMATCH + ); + } + } + $nameIdData[$attr] = $nameId->getAttribute($attr); + } + } + } + + return $nameIdData; + } + + /** + * Gets the NameID provided by the SAML response from the IdP. + * + * @return string|null Name ID Value + * + * @throws ValidationError + */ + public function getNameId() + { + $nameIdvalue = null; + $nameIdData = $this->getNameIdData(); + if (!empty($nameIdData) && isset($nameIdData['Value'])) { + $nameIdvalue = $nameIdData['Value']; + } + return $nameIdvalue; + } + + /** + * Gets the NameID Format provided by the SAML response from the IdP. + * + * @return string|null Name ID Format + * + * @throws ValidationError + */ + public function getNameIdFormat() + { + $nameIdFormat = null; + $nameIdData = $this->getNameIdData(); + if (!empty($nameIdData) && isset($nameIdData['Format'])) { + $nameIdFormat = $nameIdData['Format']; + } + return $nameIdFormat; + } + + /** + * Gets the NameID NameQualifier provided by the SAML response from the IdP. + * + * @return string|null Name ID NameQualifier + * + * @throws ValidationError + */ + public function getNameIdNameQualifier() + { + $nameIdNameQualifier = null; + $nameIdData = $this->getNameIdData(); + if (!empty($nameIdData) && isset($nameIdData['NameQualifier'])) { + $nameIdNameQualifier = $nameIdData['NameQualifier']; + } + return $nameIdNameQualifier; + } + + /** + * Gets the NameID SP NameQualifier provided by the SAML response from the IdP. + * + * @return string|null NameID SP NameQualifier + * + * @throws ValidationError + */ + public function getNameIdSPNameQualifier() + { + $nameIdSPNameQualifier = null; + $nameIdData = $this->getNameIdData(); + if (!empty($nameIdData) && isset($nameIdData['SPNameQualifier'])) { + $nameIdSPNameQualifier = $nameIdData['SPNameQualifier']; + } + return $nameIdSPNameQualifier; + } + + /** + * Gets the SessionNotOnOrAfter from the AuthnStatement. + * Could be used to set the local session expiration + * + * @return int|null The SessionNotOnOrAfter value + * + * @throws Exception + */ + public function getSessionNotOnOrAfter() + { + $notOnOrAfter = null; + $entries = $this->_queryAssertion('/saml:AuthnStatement[@SessionNotOnOrAfter]'); + if ($entries->length !== 0) { + $notOnOrAfter = Utils::parseSAML2Time($entries->item(0)->getAttribute('SessionNotOnOrAfter')); + } + return $notOnOrAfter; + } + + /** + * Gets the SessionIndex from the AuthnStatement. + * Could be used to be stored in the local session in order + * to be used in a future Logout Request that the SP could + * send to the SP, to set what specific session must be deleted + * + * @return string|null The SessionIndex value + */ + public function getSessionIndex() + { + $sessionIndex = null; + $entries = $this->_queryAssertion('/saml:AuthnStatement[@SessionIndex]'); + if ($entries->length !== 0) { + $sessionIndex = $entries->item(0)->getAttribute('SessionIndex'); + } + return $sessionIndex; + } + + /** + * Gets the Attributes from the AttributeStatement element. + * + * @return array The attributes of the SAML Assertion + * + * @throws ValidationError + */ + public function getAttributes() + { + return $this->_getAttributesByKeyName('Name'); + } + + /** + * Gets the Attributes from the AttributeStatement element using their FriendlyName. + * + * @return array The attributes of the SAML Assertion + * + * @throws ValidationError + */ + public function getAttributesWithFriendlyName() + { + return $this->_getAttributesByKeyName('FriendlyName'); + } + + /** + * @param string $keyName + * + * @return array + * + * @throws ValidationError + */ + private function _getAttributesByKeyName($keyName = "Name") + { + $attributes = array(); + $entries = $this->_queryAssertion('/saml:AttributeStatement/saml:Attribute'); + /** @var $entry DOMNode */ + foreach ($entries as $entry) { + $attributeKeyNode = $entry->attributes->getNamedItem($keyName); + if ($attributeKeyNode === null) { + continue; + } + $attributeKeyName = $attributeKeyNode->nodeValue; + if (in_array($attributeKeyName, array_keys($attributes))) { + throw new ValidationError( + "Found an Attribute element with duplicated ".$keyName, + ValidationError::DUPLICATED_ATTRIBUTE_NAME_FOUND + ); + } + $attributeValues = array(); + foreach ($entry->childNodes as $childNode) { + $tagName = ($childNode->prefix ? $childNode->prefix.':' : '') . 'AttributeValue'; + if ($childNode->nodeType == XML_ELEMENT_NODE && $childNode->tagName === $tagName) { + $attributeValues[] = $childNode->nodeValue; + } + } + $attributes[$attributeKeyName] = $attributeValues; + } + return $attributes; + } + + /** + * Verifies that the document only contains a single Assertion (encrypted or not). + * + * @return bool TRUE if the document passes. + */ + public function validateNumAssertions() + { + $encryptedAssertionNodes = $this->document->getElementsByTagName('EncryptedAssertion'); + $assertionNodes = $this->document->getElementsByTagName('Assertion'); + + $valid = $assertionNodes->length + $encryptedAssertionNodes->length == 1; + + if ($this->encrypted) { + $assertionNodes = $this->decryptedDocument->getElementsByTagName('Assertion'); + $valid = $valid && $assertionNodes->length == 1; + } + + return $valid; + } + + /** + * Verifies the signature nodes: + * - Checks that are Response or Assertion + * - Check that IDs and reference URI are unique and consistent. + * + * @return array Signed element tags + * + * @throws ValidationError + */ + public function processSignedElements() + { + $signedElements = array(); + $verifiedSeis = array(); + $verifiedIds = array(); + + if ($this->encrypted) { + $signNodes = $this->decryptedDocument->getElementsByTagName('Signature'); + } else { + $signNodes = $this->document->getElementsByTagName('Signature'); + } + foreach ($signNodes as $signNode) { + $responseTag = '{'.Constants::NS_SAMLP.'}Response'; + $assertionTag = '{'.Constants::NS_SAML.'}Assertion'; + + $signedElement = '{'.$signNode->parentNode->namespaceURI.'}'.$signNode->parentNode->localName; + + if ($signedElement != $responseTag && $signedElement != $assertionTag) { + throw new ValidationError( + "Invalid Signature Element $signedElement SAML Response rejected", + ValidationError::WRONG_SIGNED_ELEMENT + ); + } + + // Check that reference URI matches the parent ID and no duplicate References or IDs + $idValue = $signNode->parentNode->getAttribute('ID'); + if (empty($idValue)) { + throw new ValidationError( + 'Signed Element must contain an ID. SAML Response rejected', + ValidationError::ID_NOT_FOUND_IN_SIGNED_ELEMENT + ); + } + + if (in_array($idValue, $verifiedIds)) { + throw new ValidationError( + 'Duplicated ID. SAML Response rejected', + ValidationError::DUPLICATED_ID_IN_SIGNED_ELEMENTS + ); + } + $verifiedIds[] = $idValue; + + $ref = $signNode->getElementsByTagName('Reference'); + if ($ref->length == 1) { + $ref = $ref->item(0); + $sei = $ref->getAttribute('URI'); + if (!empty($sei)) { + $sei = substr($sei, 1); + + if ($sei != $idValue) { + throw new ValidationError( + 'Found an invalid Signed Element. SAML Response rejected', + ValidationError::INVALID_SIGNED_ELEMENT + ); + } + + if (in_array($sei, $verifiedSeis)) { + throw new ValidationError( + 'Duplicated Reference URI. SAML Response rejected', + ValidationError::DUPLICATED_REFERENCE_IN_SIGNED_ELEMENTS + ); + } + $verifiedSeis[] = $sei; + } + } else { + throw new ValidationError( + 'Unexpected number of Reference nodes found for signature. SAML Response rejected.', + ValidationError::UNEXPECTED_REFERENCE + ); + } + $signedElements[] = $signedElement; + } + + // Check SignedElements + if (!empty($signedElements) && !$this->validateSignedElements($signedElements)) { + throw new ValidationError( + 'Found an unexpected Signature Element. SAML Response rejected', + ValidationError::UNEXPECTED_SIGNED_ELEMENTS + ); + } + return $signedElements; + } + + /** + * Verifies that the document is still valid according Conditions Element. + * + * @return bool + * + * @throws Exception + * @throws ValidationError + */ + public function validateTimestamps() + { + if ($this->encrypted) { + $document = $this->decryptedDocument; + } else { + $document = $this->document; + } + + $timestampNodes = $document->getElementsByTagName('Conditions'); + for ($i = 0; $i < $timestampNodes->length; $i++) { + $nbAttribute = $timestampNodes->item($i)->attributes->getNamedItem("NotBefore"); + $naAttribute = $timestampNodes->item($i)->attributes->getNamedItem("NotOnOrAfter"); + if ($nbAttribute && Utils::parseSAML2Time($nbAttribute->textContent) > time() + Constants::ALLOWED_CLOCK_DRIFT) { + throw new ValidationError( + 'Could not validate timestamp: not yet valid. Check system clock.', + ValidationError::ASSERTION_TOO_EARLY + ); + } + if ($naAttribute && Utils::parseSAML2Time($naAttribute->textContent) + Constants::ALLOWED_CLOCK_DRIFT <= time()) { + throw new ValidationError( + 'Could not validate timestamp: expired. Check system clock.', + ValidationError::ASSERTION_EXPIRED + ); + } + } + return true; + } + + /** + * Verifies that the document has the expected signed nodes. + * + * @param array $signedElements Signed elements + * + * @return bool + * + * @throws ValidationError + */ + public function validateSignedElements($signedElements) + { + if (count($signedElements) > 2) { + return false; + } + + $responseTag = '{'.Constants::NS_SAMLP.'}Response'; + $assertionTag = '{'.Constants::NS_SAML.'}Assertion'; + + $ocurrence = array_count_values($signedElements); + if ((in_array($responseTag, $signedElements) && $ocurrence[$responseTag] > 1) + || (in_array($assertionTag, $signedElements) && $ocurrence[$assertionTag] > 1) + || !in_array($responseTag, $signedElements) && !in_array($assertionTag, $signedElements) + ) { + return false; + } + + // Check that the signed elements found here, are the ones that will be verified + // by Utils->validateSign() + if (in_array($responseTag, $signedElements)) { + $expectedSignatureNodes = Utils::query($this->document, Utils::RESPONSE_SIGNATURE_XPATH); + if ($expectedSignatureNodes->length != 1) { + throw new ValidationError( + "Unexpected number of Response signatures found. SAML Response rejected.", + ValidationError::WRONG_NUMBER_OF_SIGNATURES_IN_RESPONSE + ); + } + } + + if (in_array($assertionTag, $signedElements)) { + $expectedSignatureNodes = $this->_query(Utils::ASSERTION_SIGNATURE_XPATH); + if ($expectedSignatureNodes->length != 1) { + throw new ValidationError( + "Unexpected number of Assertion signatures found. SAML Response rejected.", + ValidationError::WRONG_NUMBER_OF_SIGNATURES_IN_ASSERTION + ); + } + } + + return true; + } + + /** + * Extracts a node from the DOMDocument (Assertion). + * + * @param string $assertionXpath Xpath Expression + * + * @return DOMNodeList The queried node + */ + protected function _queryAssertion($assertionXpath) + { + if ($this->encrypted) { + $xpath = new DOMXPath($this->decryptedDocument); + } else { + $xpath = new DOMXPath($this->document); + } + + $xpath->registerNamespace('samlp', Constants::NS_SAMLP); + $xpath->registerNamespace('saml', Constants::NS_SAML); + $xpath->registerNamespace('ds', Constants::NS_DS); + $xpath->registerNamespace('xenc', Constants::NS_XENC); + + $assertionNode = '/samlp:Response/saml:Assertion'; + $signatureQuery = $assertionNode . '/ds:Signature/ds:SignedInfo/ds:Reference'; + $assertionReferenceNode = $xpath->query($signatureQuery)->item(0); + if (!$assertionReferenceNode) { + // is the response signed as a whole? + $signatureQuery = '/samlp:Response/ds:Signature/ds:SignedInfo/ds:Reference'; + $responseReferenceNode = $xpath->query($signatureQuery)->item(0); + if ($responseReferenceNode) { + $uri = $responseReferenceNode->attributes->getNamedItem('URI')->nodeValue; + if (empty($uri)) { + $id = $responseReferenceNode->parentNode->parentNode->parentNode->attributes->getNamedItem('ID')->nodeValue; + } else { + $id = substr($responseReferenceNode->attributes->getNamedItem('URI')->nodeValue, 1); + } + $nameQuery = "/samlp:Response[@ID='$id']/saml:Assertion" . $assertionXpath; + } else { + $nameQuery = "/samlp:Response/saml:Assertion" . $assertionXpath; + } + } else { + $uri = $assertionReferenceNode->attributes->getNamedItem('URI')->nodeValue; + if (empty($uri)) { + $id = $assertionReferenceNode->parentNode->parentNode->parentNode->attributes->getNamedItem('ID')->nodeValue; + } else { + $id = substr($assertionReferenceNode->attributes->getNamedItem('URI')->nodeValue, 1); + } + $nameQuery = $assertionNode."[@ID='$id']" . $assertionXpath; + } + + return $xpath->query($nameQuery); + } + + /** + * Extracts nodes that match the query from the DOMDocument (Response Menssage) + * + * @param string $query Xpath Expression + * + * @return DOMNodeList The queried nodes + */ + private function _query($query) + { + if ($this->encrypted) { + return Utils::query($this->decryptedDocument, $query); + } else { + return Utils::query($this->document, $query); + } + } + + /** + * Decrypts the Assertion (DOMDocument) + * + * @param \DomNode $dom DomDocument + * + * @return DOMDocument Decrypted Assertion + * + * @throws Exception + * @throws ValidationError + */ + protected function decryptAssertion(\DomNode $dom) + { + $pem = $this->_settings->getSPkey(); + + if (empty($pem)) { + throw new Error( + "No private key available, check settings", + Error::PRIVATE_KEY_NOT_FOUND + ); + } + + $objenc = new XMLSecEnc(); + $encData = $objenc->locateEncryptedData($dom); + if (!$encData) { + throw new ValidationError( + "Cannot locate encrypted assertion", + ValidationError::MISSING_ENCRYPTED_ELEMENT + ); + } + + $objenc->setNode($encData); + $objenc->type = $encData->getAttribute("Type"); + if (!$objKey = $objenc->locateKey()) { + throw new ValidationError( + "Unknown algorithm", + ValidationError::KEY_ALGORITHM_ERROR + ); + } + + $key = null; + if ($objKeyInfo = $objenc->locateKeyInfo($objKey)) { + if ($objKeyInfo->isEncrypted) { + $objencKey = $objKeyInfo->encryptedCtx; + $objKeyInfo->loadKey($pem, false, false); + $key = $objencKey->decryptKey($objKeyInfo); + } else { + // symmetric encryption key support + $objKeyInfo->loadKey($pem, false, false); + } + } + + if (empty($objKey->key)) { + $objKey->loadKey($key); + } + + $decryptedXML = $objenc->decryptNode($objKey, false); + $decrypted = new DOMDocument(); + $check = Utils::loadXML($decrypted, $decryptedXML); + if ($check === false) { + throw new Exception('Error: string from decrypted assertion could not be loaded into a XML document'); + } + if ($encData->parentNode instanceof DOMDocument) { + return $decrypted; + } else { + $decrypted = $decrypted->documentElement; + $encryptedAssertion = $encData->parentNode; + $container = $encryptedAssertion->parentNode; + + // Fix possible issue with saml namespace + if (!$decrypted->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:saml') + && !$decrypted->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:saml2') + && !$decrypted->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns') + && !$container->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:saml') + && !$container->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:saml2') + ) { + if (strpos($encryptedAssertion->tagName, 'saml2:') !== false) { + $ns = 'xmlns:saml2'; + } else if (strpos($encryptedAssertion->tagName, 'saml:') !== false) { + $ns = 'xmlns:saml'; + } else { + $ns = 'xmlns'; + } + $decrypted->setAttributeNS('http://www.w3.org/2000/xmlns/', $ns, Constants::NS_SAML); + } + + Utils::treeCopyReplace($encryptedAssertion, $decrypted); + + // Rebuild the DOM will fix issues with namespaces as well + $dom = new DOMDocument(); + return Utils::loadXML($dom, $container->ownerDocument->saveXML()); + } + } + + /** + * After execute a validation process, if fails this method returns the cause + * + * @return Exception|null Cause + */ + public function getErrorException() + { + return $this->_error; + } + + /** + * After execute a validation process, if fails this method returns the cause + * + * @return null|string Error reason + */ + public function getError() + { + $errorMsg = null; + if (isset($this->_error)) { + $errorMsg = htmlentities($this->_error->getMessage()); + } + return $errorMsg; + } + + /** + * Returns the SAML Response document (If contains an encrypted assertion, decrypts it) + * + * @return DomDocument SAML Response + */ + public function getXMLDocument() + { + if ($this->encrypted) { + return $this->decryptedDocument; + } else { + return $this->document; + } + } +} diff --git a/vendor/onelogin/php-saml/src/Saml2/Settings.php b/vendor/onelogin/php-saml/src/Saml2/Settings.php new file mode 100644 index 0000000000..ca7b42f5a1 --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/Settings.php @@ -0,0 +1,1092 @@ + + * @license MIT https://github.com/onelogin/php-saml/blob/master/LICENSE + * @link https://github.com/onelogin/php-saml + */ + +namespace OneLogin\Saml2; + +use RobRichards\XMLSecLibs\XMLSecurityKey; +use RobRichards\XMLSecLibs\XMLSecurityDSig; + +use DOMDocument; +use Exception; + +/** + * Configuration of the OneLogin PHP Toolkit + */ +class Settings +{ + /** + * List of paths. + * + * @var array + */ + private $_paths = array(); + + /** + * @var string + */ + private $_baseurl; + + /** + * Strict. If active, PHP Toolkit will reject unsigned or unencrypted messages + * if it expects them signed or encrypted. If not, the messages will be accepted + * and some security issues will be also relaxed. + * + * @var bool + */ + private $_strict = true; + + /** + * Activate debug mode + * + * @var bool + */ + private $_debug = false; + + /** + * SP data. + * + * @var array + */ + private $_sp = array(); + + /** + * IdP data. + * + * @var array + */ + private $_idp = array(); + + /** + * Compression settings that determine + * whether gzip compression should be used. + * + * @var array + */ + private $_compress = array(); + + /** + * Security Info related to the SP. + * + * @var array + */ + private $_security = array(); + + /** + * Setting contacts. + * + * @var array + */ + private $_contacts = array(); + + /** + * Setting organization. + * + * @var array + */ + private $_organization = array(); + + /** + * Setting errors. + * + * @var array + */ + private $_errors = array(); + + /** + * Valitate SP data only flag + * + * @var bool + */ + private $_spValidationOnly = false; + + /** + * Initializes the settings: + * - Sets the paths of the different folders + * - Loads settings info from settings file or array/object provided + * + * @param array|null $settings SAML Toolkit Settings + * @param bool $spValidationOnly Validate or not the IdP data + * + * @throws Error If any settings parameter is invalid + * @throws Exception If Settings is incorrectly supplied + */ + public function __construct(array $settings = null, $spValidationOnly = false) + { + $this->_spValidationOnly = $spValidationOnly; + $this->_loadPaths(); + + if (!isset($settings)) { + if (!$this->_loadSettingsFromFile()) { + throw new Error( + 'Invalid file settings: %s', + Error::SETTINGS_INVALID, + array(implode(', ', $this->_errors)) + ); + } + $this->_addDefaultValues(); + } else { + if (!$this->_loadSettingsFromArray($settings)) { + throw new Error( + 'Invalid array settings: %s', + Error::SETTINGS_INVALID, + array(implode(', ', $this->_errors)) + ); + } + } + + $this->formatIdPCert(); + $this->formatSPCert(); + $this->formatSPKey(); + $this->formatSPCertNew(); + $this->formatIdPCertMulti(); + } + + /** + * Sets the paths of the different folders + * @suppress PhanUndeclaredConstant + */ + private function _loadPaths() + { + $basePath = dirname(dirname(__DIR__)) . '/'; + $this->_paths = array( + 'base' => $basePath, + 'config' => $basePath, + 'cert' => $basePath.'certs/', + 'lib' => $basePath.'src/' + ); + + if (defined('ONELOGIN_CUSTOMPATH')) { + $this->_paths['config'] = ONELOGIN_CUSTOMPATH; + $this->_paths['cert'] = ONELOGIN_CUSTOMPATH . 'certs/'; + } + } + + /** + * Returns base path. + * + * @return string The base toolkit folder path + */ + public function getBasePath() + { + return $this->_paths['base']; + } + + /** + * Returns cert path. + * + * @return string The cert folder path + */ + public function getCertPath() + { + return $this->_paths['cert']; + } + + /** + * Returns config path. + * + * @return string The config folder path + */ + public function getConfigPath() + { + return $this->_paths['config']; + } + + /** + * Returns lib path. + * + * @return string The library folder path + */ + public function getLibPath() + { + return $this->_paths['lib']; + } + + /** + * Returns schema path. + * + * @return string The external library folder path + */ + public function getSchemasPath() + { + return $this->_paths['lib'].'schemas/'; + } + + /** + * Loads settings info from a settings Array + * + * @param array $settings SAML Toolkit Settings + * + * @return bool True if the settings info is valid + */ + private function _loadSettingsFromArray(array $settings) + { + if (isset($settings['sp'])) { + $this->_sp = $settings['sp']; + } + if (isset($settings['idp'])) { + $this->_idp = $settings['idp']; + } + + $errors = $this->checkSettings($settings); + if (empty($errors)) { + $this->_errors = array(); + + if (isset($settings['strict'])) { + $this->_strict = $settings['strict']; + } + if (isset($settings['debug'])) { + $this->_debug = $settings['debug']; + } + + if (isset($settings['baseurl'])) { + $this->_baseurl = $settings['baseurl']; + } + + if (isset($settings['compress'])) { + $this->_compress = $settings['compress']; + } + + if (isset($settings['security'])) { + $this->_security = $settings['security']; + } + + if (isset($settings['contactPerson'])) { + $this->_contacts = $settings['contactPerson']; + } + + if (isset($settings['organization'])) { + $this->_organization = $settings['organization']; + } + + $this->_addDefaultValues(); + return true; + } else { + $this->_errors = $errors; + return false; + } + } + + /** + * Loads settings info from the settings file + * + * @return bool True if the settings info is valid + * + * @throws Error + * + * @suppress PhanUndeclaredVariable + */ + private function _loadSettingsFromFile() + { + $filename = $this->getConfigPath().'settings.php'; + + if (!file_exists($filename)) { + throw new Error( + 'Settings file not found: %s', + Error::SETTINGS_FILE_NOT_FOUND, + array($filename) + ); + } + + /** @var array $settings */ + include $filename; + + // Add advance_settings if exists + $advancedFilename = $this->getConfigPath().'advanced_settings.php'; + + if (file_exists($advancedFilename)) { + /** @var array $advancedSettings */ + include $advancedFilename; + $settings = array_merge($settings, $advancedSettings); + } + + + return $this->_loadSettingsFromArray($settings); + } + + /** + * Add default values if the settings info is not complete + */ + private function _addDefaultValues() + { + if (!isset($this->_sp['assertionConsumerService']['binding'])) { + $this->_sp['assertionConsumerService']['binding'] = Constants::BINDING_HTTP_POST; + } + if (isset($this->_sp['singleLogoutService']) && !isset($this->_sp['singleLogoutService']['binding'])) { + $this->_sp['singleLogoutService']['binding'] = Constants::BINDING_HTTP_REDIRECT; + } + + if (!isset($this->_compress['requests'])) { + $this->_compress['requests'] = true; + } + + if (!isset($this->_compress['responses'])) { + $this->_compress['responses'] = true; + } + + // Related to nameID + if (!isset($this->_sp['NameIDFormat'])) { + $this->_sp['NameIDFormat'] = Constants::NAMEID_UNSPECIFIED; + } + if (!isset($this->_security['nameIdEncrypted'])) { + $this->_security['nameIdEncrypted'] = false; + } + if (!isset($this->_security['requestedAuthnContext'])) { + $this->_security['requestedAuthnContext'] = true; + } + + // sign provided + if (!isset($this->_security['authnRequestsSigned'])) { + $this->_security['authnRequestsSigned'] = false; + } + if (!isset($this->_security['logoutRequestSigned'])) { + $this->_security['logoutRequestSigned'] = false; + } + if (!isset($this->_security['logoutResponseSigned'])) { + $this->_security['logoutResponseSigned'] = false; + } + if (!isset($this->_security['signMetadata'])) { + $this->_security['signMetadata'] = false; + } + + // sign expected + if (!isset($this->_security['wantMessagesSigned'])) { + $this->_security['wantMessagesSigned'] = false; + } + if (!isset($this->_security['wantAssertionsSigned'])) { + $this->_security['wantAssertionsSigned'] = false; + } + + // NameID element expected + if (!isset($this->_security['wantNameId'])) { + $this->_security['wantNameId'] = true; + } + + // Relax Destination validation + if (!isset($this->_security['relaxDestinationValidation'])) { + $this->_security['relaxDestinationValidation'] = false; + } + + // encrypt expected + if (!isset($this->_security['wantAssertionsEncrypted'])) { + $this->_security['wantAssertionsEncrypted'] = false; + } + if (!isset($this->_security['wantNameIdEncrypted'])) { + $this->_security['wantNameIdEncrypted'] = false; + } + + // XML validation + if (!isset($this->_security['wantXMLValidation'])) { + $this->_security['wantXMLValidation'] = true; + } + + // SignatureAlgorithm + if (!isset($this->_security['signatureAlgorithm'])) { + $this->_security['signatureAlgorithm'] = XMLSecurityKey::RSA_SHA256; + } + + // DigestAlgorithm + if (!isset($this->_security['digestAlgorithm'])) { + $this->_security['digestAlgorithm'] = XMLSecurityDSig::SHA256; + } + + if (!isset($this->_security['lowercaseUrlencoding'])) { + $this->_security['lowercaseUrlencoding'] = false; + } + + // Certificates / Private key /Fingerprint + if (!isset($this->_idp['x509cert'])) { + $this->_idp['x509cert'] = ''; + } + if (!isset($this->_idp['certFingerprint'])) { + $this->_idp['certFingerprint'] = ''; + } + if (!isset($this->_idp['certFingerprintAlgorithm'])) { + $this->_idp['certFingerprintAlgorithm'] = 'sha1'; + } + + if (!isset($this->_sp['x509cert'])) { + $this->_sp['x509cert'] = ''; + } + if (!isset($this->_sp['privateKey'])) { + $this->_sp['privateKey'] = ''; + } + } + + /** + * Checks the settings info. + * + * @param array $settings Array with settings data + * + * @return array $errors Errors found on the settings data + */ + public function checkSettings(array $settings) + { + if (empty($settings)) { + $errors = array('invalid_syntax'); + } else { + $errors = array(); + if (!$this->_spValidationOnly) { + $idpErrors = $this->checkIdPSettings($settings); + $errors = array_merge($idpErrors, $errors); + } + $spErrors = $this->checkSPSettings($settings); + $errors = array_merge($spErrors, $errors); + + $compressErrors = $this->checkCompressionSettings($settings); + $errors = array_merge($compressErrors, $errors); + } + + return $errors; + } + + /** + * Checks the compression settings info. + * + * @param array $settings Array with settings data + * + * @return array $errors Errors found on the settings data + */ + public function checkCompressionSettings($settings) + { + $errors = array(); + + if (isset($settings['compress'])) { + if (!is_array($settings['compress'])) { + $errors[] = "invalid_syntax"; + } else if (isset($settings['compress']['requests']) + && $settings['compress']['requests'] !== true + && $settings['compress']['requests'] !== false + ) { + $errors[] = "'compress'=>'requests' values must be true or false."; + } else if (isset($settings['compress']['responses']) + && $settings['compress']['responses'] !== true + && $settings['compress']['responses'] !== false + ) { + $errors[] = "'compress'=>'responses' values must be true or false."; + } + } + return $errors; + } + + /** + * Checks the IdP settings info. + * + * @param array $settings Array with settings data + * + * @return array $errors Errors found on the IdP settings data + */ + public function checkIdPSettings(array $settings) + { + if (empty($settings)) { + return array('invalid_syntax'); + } + + $errors = array(); + + if (!isset($settings['idp']) || empty($settings['idp'])) { + $errors[] = 'idp_not_found'; + } else { + $idp = $settings['idp']; + if (!isset($idp['entityId']) || empty($idp['entityId'])) { + $errors[] = 'idp_entityId_not_found'; + } + + if (!isset($idp['singleSignOnService']) + || !isset($idp['singleSignOnService']['url']) + || empty($idp['singleSignOnService']['url']) + ) { + $errors[] = 'idp_sso_not_found'; + } else if (!filter_var($idp['singleSignOnService']['url'], FILTER_VALIDATE_URL)) { + $errors[] = 'idp_sso_url_invalid'; + } + + if (isset($idp['singleLogoutService']) + && isset($idp['singleLogoutService']['url']) + && !empty($idp['singleLogoutService']['url']) + && !filter_var($idp['singleLogoutService']['url'], FILTER_VALIDATE_URL) + ) { + $errors[] = 'idp_slo_url_invalid'; + } + + if (isset($idp['singleLogoutService']) + && isset($idp['singleLogoutService']['responseUrl']) + && !empty($idp['singleLogoutService']['responseUrl']) + && !filter_var($idp['singleLogoutService']['responseUrl'], FILTER_VALIDATE_URL) + ) { + $errors[] = 'idp_slo_response_url_invalid'; + } + + if (isset($settings['security'])) { + $security = $settings['security']; + + $existsX509 = isset($idp['x509cert']) && !empty($idp['x509cert']); + $existsMultiX509Sign = isset($idp['x509certMulti']) && isset($idp['x509certMulti']['signing']) && !empty($idp['x509certMulti']['signing']); + $existsMultiX509Enc = isset($idp['x509certMulti']) && isset($idp['x509certMulti']['encryption']) && !empty($idp['x509certMulti']['encryption']); + + $existsFingerprint = isset($idp['certFingerprint']) && !empty($idp['certFingerprint']); + if (!($existsX509 || $existsFingerprint || $existsMultiX509Sign) + ) { + $errors[] = 'idp_cert_or_fingerprint_not_found_and_required'; + } + if ((isset($security['nameIdEncrypted']) && $security['nameIdEncrypted'] == true) + && !($existsX509 || $existsMultiX509Enc) + ) { + $errors[] = 'idp_cert_not_found_and_required'; + } + } + } + + return $errors; + } + + /** + * Checks the SP settings info. + * + * @param array $settings Array with settings data + * + * @return array $errors Errors found on the SP settings data + */ + public function checkSPSettings(array $settings) + { + if (empty($settings)) { + return array('invalid_syntax'); + } + + $errors = array(); + + if (!isset($settings['sp']) || empty($settings['sp'])) { + $errors[] = 'sp_not_found'; + } else { + $sp = $settings['sp']; + $security = array(); + if (isset($settings['security'])) { + $security = $settings['security']; + } + + if (!isset($sp['entityId']) || empty($sp['entityId'])) { + $errors[] = 'sp_entityId_not_found'; + } + + if (!isset($sp['assertionConsumerService']) + || !isset($sp['assertionConsumerService']['url']) + || empty($sp['assertionConsumerService']['url']) + ) { + $errors[] = 'sp_acs_not_found'; + } else if (!filter_var($sp['assertionConsumerService']['url'], FILTER_VALIDATE_URL)) { + $errors[] = 'sp_acs_url_invalid'; + } + + if (isset($sp['singleLogoutService']) + && isset($sp['singleLogoutService']['url']) + && !filter_var($sp['singleLogoutService']['url'], FILTER_VALIDATE_URL) + ) { + $errors[] = 'sp_sls_url_invalid'; + } + + if (isset($security['signMetadata']) && is_array($security['signMetadata'])) { + if ((!isset($security['signMetadata']['keyFileName']) + || !isset($security['signMetadata']['certFileName'])) && + (!isset($security['signMetadata']['privateKey']) + || !isset($security['signMetadata']['x509cert'])) + ) { + $errors[] = 'sp_signMetadata_invalid'; + } + } + + if (((isset($security['authnRequestsSigned']) && $security['authnRequestsSigned'] == true) + || (isset($security['logoutRequestSigned']) && $security['logoutRequestSigned'] == true) + || (isset($security['logoutResponseSigned']) && $security['logoutResponseSigned'] == true) + || (isset($security['wantAssertionsEncrypted']) && $security['wantAssertionsEncrypted'] == true) + || (isset($security['wantNameIdEncrypted']) && $security['wantNameIdEncrypted'] == true)) + && !$this->checkSPCerts() + ) { + $errors[] = 'sp_certs_not_found_and_required'; + } + } + + if (isset($settings['contactPerson'])) { + $types = array_keys($settings['contactPerson']); + $validTypes = array('technical', 'support', 'administrative', 'billing', 'other'); + foreach ($types as $type) { + if (!in_array($type, $validTypes)) { + $errors[] = 'contact_type_invalid'; + break; + } + } + + foreach ($settings['contactPerson'] as $type => $contact) { + if (!isset($contact['givenName']) || empty($contact['givenName']) + || !isset($contact['emailAddress']) || empty($contact['emailAddress']) + ) { + $errors[] = 'contact_not_enought_data'; + break; + } + } + } + + if (isset($settings['organization'])) { + foreach ($settings['organization'] as $organization) { + if (!isset($organization['name']) || empty($organization['name']) + || !isset($organization['displayname']) || empty($organization['displayname']) + || !isset($organization['url']) || empty($organization['url']) + ) { + $errors[] = 'organization_not_enought_data'; + break; + } + } + } + + return $errors; + } + + /** + * Checks if the x509 certs of the SP exists and are valid. + * + * @return bool + */ + public function checkSPCerts() + { + $key = $this->getSPkey(); + $cert = $this->getSPcert(); + return (!empty($key) && !empty($cert)); + } + + /** + * Returns the x509 private key of the SP. + * + * @return string SP private key + */ + public function getSPkey() + { + $key = null; + if (isset($this->_sp['privateKey']) && !empty($this->_sp['privateKey'])) { + $key = $this->_sp['privateKey']; + } else { + $keyFile = $this->_paths['cert'].'sp.key'; + + if (file_exists($keyFile)) { + $key = file_get_contents($keyFile); + } + } + return $key; + } + + /** + * Returns the x509 public cert of the SP. + * + * @return string SP public cert + */ + public function getSPcert() + { + $cert = null; + + if (isset($this->_sp['x509cert']) && !empty($this->_sp['x509cert'])) { + $cert = $this->_sp['x509cert']; + } else { + $certFile = $this->_paths['cert'].'sp.crt'; + + if (file_exists($certFile)) { + $cert = file_get_contents($certFile); + } + } + return $cert; + } + + /** + * Returns the x509 public of the SP that is + * planed to be used soon instead the other + * public cert + * + * @return string SP public cert New + */ + public function getSPcertNew() + { + $cert = null; + + if (isset($this->_sp['x509certNew']) && !empty($this->_sp['x509certNew'])) { + $cert = $this->_sp['x509certNew']; + } else { + $certFile = $this->_paths['cert'].'sp_new.crt'; + + if (file_exists($certFile)) { + $cert = file_get_contents($certFile); + } + } + return $cert; + } + + /** + * Gets the IdP data. + * + * @return array IdP info + */ + public function getIdPData() + { + return $this->_idp; + } + + /** + * Gets the SP data. + * + * @return array SP info + */ + public function getSPData() + { + return $this->_sp; + } + + /** + * Gets security data. + * + * @return array SP info + */ + public function getSecurityData() + { + return $this->_security; + } + + /** + * Gets contact data. + * + * @return array SP info + */ + public function getContacts() + { + return $this->_contacts; + } + + /** + * Gets organization data. + * + * @return array SP info + */ + public function getOrganization() + { + return $this->_organization; + } + + /** + * Should SAML requests be compressed? + * + * @return bool Yes/No as True/False + */ + public function shouldCompressRequests() + { + return $this->_compress['requests']; + } + + /** + * Should SAML responses be compressed? + * + * @return bool Yes/No as True/False + */ + public function shouldCompressResponses() + { + return $this->_compress['responses']; + } + + /** + * Gets the SP metadata. The XML representation. + * + * @param bool $alwaysPublishEncryptionCert When 'true', the returned + * metadata will always include an 'encryption' KeyDescriptor. Otherwise, + * the 'encryption' KeyDescriptor will only be included if + * $advancedSettings['security']['wantNameIdEncrypted'] or + * $advancedSettings['security']['wantAssertionsEncrypted'] are enabled. + * @param int|null $validUntil Metadata's valid time + * @param int|null $cacheDuration Duration of the cache in seconds + * + * @return string SP metadata (xml) + * @throws Exception + * @throws Error + */ + public function getSPMetadata($alwaysPublishEncryptionCert = false, $validUntil = null, $cacheDuration = null) + { + $metadata = Metadata::builder($this->_sp, $this->_security['authnRequestsSigned'], $this->_security['wantAssertionsSigned'], $validUntil, $cacheDuration, $this->getContacts(), $this->getOrganization()); + + $certNew = $this->getSPcertNew(); + if (!empty($certNew)) { + $metadata = Metadata::addX509KeyDescriptors( + $metadata, + $certNew, + $alwaysPublishEncryptionCert || $this->_security['wantNameIdEncrypted'] || $this->_security['wantAssertionsEncrypted'] + ); + } + + $cert = $this->getSPcert(); + if (!empty($cert)) { + $metadata = Metadata::addX509KeyDescriptors( + $metadata, + $cert, + $alwaysPublishEncryptionCert || $this->_security['wantNameIdEncrypted'] || $this->_security['wantAssertionsEncrypted'] + ); + } + + //Sign Metadata + if (isset($this->_security['signMetadata']) && $this->_security['signMetadata'] != false) { + if ($this->_security['signMetadata'] === true) { + $keyMetadata = $this->getSPkey(); + $certMetadata = $cert; + + if (!$keyMetadata) { + throw new Error( + 'SP Private key not found.', + Error::PRIVATE_KEY_FILE_NOT_FOUND + ); + } + + if (!$certMetadata) { + throw new Error( + 'SP Public cert not found.', + Error::PUBLIC_CERT_FILE_NOT_FOUND + ); + } + } else if (isset($this->_security['signMetadata']['keyFileName']) && + isset($this->_security['signMetadata']['certFileName'])) { + $keyFileName = $this->_security['signMetadata']['keyFileName']; + $certFileName = $this->_security['signMetadata']['certFileName']; + + $keyMetadataFile = $this->_paths['cert'].$keyFileName; + $certMetadataFile = $this->_paths['cert'].$certFileName; + + if (!file_exists($keyMetadataFile)) { + throw new Error( + 'SP Private key file not found: %s', + Error::PRIVATE_KEY_FILE_NOT_FOUND, + array($keyMetadataFile) + ); + } + + if (!file_exists($certMetadataFile)) { + throw new Error( + 'SP Public cert file not found: %s', + Error::PUBLIC_CERT_FILE_NOT_FOUND, + array($certMetadataFile) + ); + } + $keyMetadata = file_get_contents($keyMetadataFile); + $certMetadata = file_get_contents($certMetadataFile); + } else if (isset($this->_security['signMetadata']['privateKey']) && + isset($this->_security['signMetadata']['x509cert'])) { + $keyMetadata = Utils::formatPrivateKey($this->_security['signMetadata']['privateKey']); + $certMetadata = Utils::formatCert($this->_security['signMetadata']['x509cert']); + if (!$keyMetadata) { + throw new Error( + 'Private key not found.', + Error::PRIVATE_KEY_FILE_NOT_FOUND + ); + } + + if (!$certMetadata) { + throw new Error( + 'Public cert not found.', + Error::PUBLIC_CERT_FILE_NOT_FOUND + ); + } + } else { + throw new Error( + 'Invalid Setting: signMetadata value of the sp is not valid', + Error::SETTINGS_INVALID_SYNTAX + ); + + } + + $signatureAlgorithm = $this->_security['signatureAlgorithm']; + $digestAlgorithm = $this->_security['digestAlgorithm']; + $metadata = Metadata::signMetadata($metadata, $keyMetadata, $certMetadata, $signatureAlgorithm, $digestAlgorithm); + } + return $metadata; + } + + /** + * Validates an XML SP Metadata. + * + * @param string $xml Metadata's XML that will be validate + * + * @return array The list of found errors + * + * @throws Exception + */ + public function validateMetadata($xml) + { + assert(is_string($xml)); + + $errors = array(); + $res = Utils::validateXML($xml, 'saml-schema-metadata-2.0.xsd', $this->_debug); + if (!$res instanceof DOMDocument) { + $errors[] = $res; + } else { + $dom = $res; + $element = $dom->documentElement; + if ($element->tagName !== 'md:EntityDescriptor') { + $errors[] = 'noEntityDescriptor_xml'; + } else { + $validUntil = $cacheDuration = $expireTime = null; + + if ($element->hasAttribute('validUntil')) { + $validUntil = Utils::parseSAML2Time($element->getAttribute('validUntil')); + } + if ($element->hasAttribute('cacheDuration')) { + $cacheDuration = $element->getAttribute('cacheDuration'); + } + + $expireTime = Utils::getExpireTime($cacheDuration, $validUntil); + if (isset($expireTime) && time() > $expireTime) { + $errors[] = 'expired_xml'; + } + } + } + + // TODO: Support Metadata Sign Validation + + return $errors; + } + + /** + * Formats the IdP cert. + */ + public function formatIdPCert() + { + if (isset($this->_idp['x509cert'])) { + $this->_idp['x509cert'] = Utils::formatCert($this->_idp['x509cert']); + } + } + + /** + * Formats the Multple IdP certs. + */ + public function formatIdPCertMulti() + { + if (isset($this->_idp['x509certMulti'])) { + if (isset($this->_idp['x509certMulti']['signing'])) { + foreach ($this->_idp['x509certMulti']['signing'] as $i => $cert) { + $this->_idp['x509certMulti']['signing'][$i] = Utils::formatCert($cert); + } + } + if (isset($this->_idp['x509certMulti']['encryption'])) { + foreach ($this->_idp['x509certMulti']['encryption'] as $i => $cert) { + $this->_idp['x509certMulti']['encryption'][$i] = Utils::formatCert($cert); + } + } + } + } + + /** + * Formats the SP cert. + */ + public function formatSPCert() + { + if (isset($this->_sp['x509cert'])) { + $this->_sp['x509cert'] = Utils::formatCert($this->_sp['x509cert']); + } + } + + /** + * Formats the SP cert. + */ + public function formatSPCertNew() + { + if (isset($this->_sp['x509certNew'])) { + $this->_sp['x509certNew'] = Utils::formatCert($this->_sp['x509certNew']); + } + } + + /** + * Formats the SP private key. + */ + public function formatSPKey() + { + if (isset($this->_sp['privateKey'])) { + $this->_sp['privateKey'] = Utils::formatPrivateKey($this->_sp['privateKey']); + } + } + + /** + * Returns an array with the errors, the array is empty when the settings is ok. + * + * @return array Errors + */ + public function getErrors() + { + return $this->_errors; + } + + /** + * Activates or deactivates the strict mode. + * + * @param bool $value Strict parameter + * + * @throws Exception + */ + public function setStrict($value) + { + if (!is_bool($value)) { + throw new Exception('Invalid value passed to setStrict()'); + } + + $this->_strict = $value; + } + + /** + * Returns if the 'strict' mode is active. + * + * @return bool Strict parameter + */ + public function isStrict() + { + return $this->_strict; + } + + /** + * Returns if the debug is active. + * + * @return bool Debug parameter + */ + public function isDebugActive() + { + return $this->_debug; + } + + /** + * Set a baseurl value. + * + * @param string $baseurl Base URL. + */ + public function setBaseURL($baseurl) + { + $this->_baseurl = $baseurl; + } + + /** + * Returns the baseurl set on the settings if any. + * + * @return null|string The baseurl + */ + public function getBaseURL() + { + return $this->_baseurl; + } + + /** + * Sets the IdP certificate. + * + * @param string $cert IdP certificate + */ + public function setIdPCert($cert) + { + $this->_idp['x509cert'] = $cert; + $this->formatIdPCert(); + } +} diff --git a/vendor/onelogin/php-saml/src/Saml2/Utils.php b/vendor/onelogin/php-saml/src/Saml2/Utils.php new file mode 100644 index 0000000000..73d169597a --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/Utils.php @@ -0,0 +1,1541 @@ + + * @license MIT https://github.com/onelogin/php-saml/blob/master/LICENSE + * @link https://github.com/onelogin/php-saml + */ + +namespace OneLogin\Saml2; + +use RobRichards\XMLSecLibs\XMLSecurityKey; +use RobRichards\XMLSecLibs\XMLSecurityDSig; +use RobRichards\XMLSecLibs\XMLSecEnc; + +use DOMDocument; +use DOMElement; +use DOMNodeList; +use DomNode; +use DOMXPath; +use Exception; + +/** + * Utils of OneLogin PHP Toolkit + * + * Defines several often used methods + */ +class Utils +{ + const RESPONSE_SIGNATURE_XPATH = "/samlp:Response/ds:Signature"; + const ASSERTION_SIGNATURE_XPATH = "/samlp:Response/saml:Assertion/ds:Signature"; + + /** + * @var bool Control if the `Forwarded-For-*` headers are used + */ + private static $_proxyVars = false; + + /** + * @var string|null + */ + private static $_host; + + /** + * @var string|null + */ + private static $_protocol; + + /** + * @var string + */ + private static $_protocolRegex = '@^https?://@i'; + + /** + * @var int|null + */ + private static $_port; + + /** + * @var string|null + */ + private static $_baseurlpath; + + /** + * This function load an XML string in a save way. + * Prevent XEE/XXE Attacks + * + * @param DOMDocument $dom The document where load the xml. + * @param string $xml The XML string to be loaded. + * + * @return DOMDocument|false $dom The result of load the XML at the DOMDocument + * + * @throws Exception + */ + public static function loadXML(DOMDocument $dom, $xml) + { + assert($dom instanceof DOMDocument); + assert(is_string($xml)); + + $oldEntityLoader = libxml_disable_entity_loader(true); + + $res = $dom->loadXML($xml); + + libxml_disable_entity_loader($oldEntityLoader); + + foreach ($dom->childNodes as $child) { + if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) { + throw new Exception( + 'Detected use of DOCTYPE/ENTITY in XML, disabled to prevent XXE/XEE attacks' + ); + } + } + + if (!$res) { + return false; + } else { + return $dom; + } + } + + /** + * This function attempts to validate an XML string against the specified schema. + * + * It will parse the string into a DOMDocument and validate this document against the schema. + * + * @param string|DOMDocument $xml The XML string or document which should be validated. + * @param string $schema The schema filename which should be used. + * @param bool $debug To disable/enable the debug mode + * + * @return string|DOMDocument $dom string that explains the problem or the DOMDocument + * + * @throws Exception + */ + public static function validateXML($xml, $schema, $debug = false) + { + assert(is_string($xml) || $xml instanceof DOMDocument); + assert(is_string($schema)); + + libxml_clear_errors(); + libxml_use_internal_errors(true); + + if ($xml instanceof DOMDocument) { + $dom = $xml; + } else { + $dom = new DOMDocument; + $dom = self::loadXML($dom, $xml); + if (!$dom) { + return 'unloaded_xml'; + } + } + + $schemaFile = __DIR__ . '/schemas/' . $schema; + $oldEntityLoader = libxml_disable_entity_loader(false); + $res = $dom->schemaValidate($schemaFile); + libxml_disable_entity_loader($oldEntityLoader); + if (!$res) { + $xmlErrors = libxml_get_errors(); + syslog(LOG_INFO, 'Error validating the metadata: '.var_export($xmlErrors, true)); + + if ($debug) { + foreach ($xmlErrors as $error) { + echo htmlentities($error->message)."\n"; + } + } + return 'invalid_xml'; + } + + return $dom; + } + + /** + * Import a node tree into a target document + * Copy it before a reference node as a sibling + * and at the end of the copy remove + * the reference node in the target document + * As it were 'replacing' it + * Leaving nested default namespaces alone + * (Standard importNode with deep copy + * mangles nested default namespaces) + * + * The reference node must not be a DomDocument + * It CAN be the top element of a document + * Returns the copied node in the target document + * + * @param DomNode $targetNode + * @param DomNode $sourceNode + * @param bool $recurse + * @return DOMNode + * @throws Exception + */ + public static function treeCopyReplace(DomNode $targetNode, DomNode $sourceNode, $recurse = false) + { + if ($targetNode->parentNode === null) { + throw new Exception('Illegal argument targetNode. It has no parentNode.'); + } + $clonedNode = $targetNode->ownerDocument->importNode($sourceNode, false); + if ($recurse) { + $resultNode = $targetNode->appendChild($clonedNode); + } else { + $resultNode = $targetNode->parentNode->insertBefore($clonedNode, $targetNode); + } + if ($sourceNode->childNodes !== null) { + foreach ($sourceNode->childNodes as $child) { + self::treeCopyReplace($resultNode, $child, true); + } + } + if (!$recurse) { + $targetNode->parentNode->removeChild($targetNode); + } + return $resultNode; + } + + /** + * Returns a x509 cert (adding header & footer if required). + * + * @param string $cert A x509 unformated cert + * @param bool $heads True if we want to include head and footer + * + * @return string $x509 Formatted cert + */ + public static function formatCert($cert, $heads = true) + { + $x509cert = str_replace(array("\x0D", "\r", "\n"), "", $cert); + if (!empty($x509cert)) { + $x509cert = str_replace('-----BEGIN CERTIFICATE-----', "", $x509cert); + $x509cert = str_replace('-----END CERTIFICATE-----', "", $x509cert); + $x509cert = str_replace(' ', '', $x509cert); + + if ($heads) { + $x509cert = "-----BEGIN CERTIFICATE-----\n".chunk_split($x509cert, 64, "\n")."-----END CERTIFICATE-----\n"; + } + + } + return $x509cert; + } + + /** + * Returns a private key (adding header & footer if required). + * + * @param string $key A private key + * @param bool $heads True if we want to include head and footer + * + * @return string $rsaKey Formatted private key + */ + public static function formatPrivateKey($key, $heads = true) + { + $key = str_replace(array("\x0D", "\r", "\n"), "", $key); + if (!empty($key)) { + if (strpos($key, '-----BEGIN PRIVATE KEY-----') !== false) { + $key = Utils::getStringBetween($key, '-----BEGIN PRIVATE KEY-----', '-----END PRIVATE KEY-----'); + $key = str_replace(' ', '', $key); + + if ($heads) { + $key = "-----BEGIN PRIVATE KEY-----\n".chunk_split($key, 64, "\n")."-----END PRIVATE KEY-----\n"; + } + } else if (strpos($key, '-----BEGIN RSA PRIVATE KEY-----') !== false) { + $key = Utils::getStringBetween($key, '-----BEGIN RSA PRIVATE KEY-----', '-----END RSA PRIVATE KEY-----'); + $key = str_replace(' ', '', $key); + + if ($heads) { + $key = "-----BEGIN RSA PRIVATE KEY-----\n".chunk_split($key, 64, "\n")."-----END RSA PRIVATE KEY-----\n"; + } + } else { + $key = str_replace(' ', '', $key); + + if ($heads) { + $key = "-----BEGIN RSA PRIVATE KEY-----\n".chunk_split($key, 64, "\n")."-----END RSA PRIVATE KEY-----\n"; + } + } + } + return $key; + } + + /** + * Extracts a substring between 2 marks + * + * @param string $str The target string + * @param string $start The initial mark + * @param string $end The end mark + * + * @return string A substring or an empty string if is not able to find the marks + * or if there is no string between the marks + */ + public static function getStringBetween($str, $start, $end) + { + $str = ' ' . $str; + $ini = strpos($str, $start); + + if ($ini == 0) { + return ''; + } + + $ini += strlen($start); + $len = strpos($str, $end, $ini) - $ini; + return substr($str, $ini, $len); + } + + /** + * Executes a redirection to the provided url (or return the target url). + * + * @param string $url The target url + * @param array $parameters Extra parameters to be passed as part of the url + * @param bool $stay True if we want to stay (returns the url string) False to redirect + * + * @return string|null $url + * + * @throws Error + */ + public static function redirect($url, array $parameters = array(), $stay = false) + { + assert(is_string($url)); + + if (substr($url, 0, 1) === '/') { + $url = self::getSelfURLhost() . $url; + } + + /** + * Verify that the URL matches the regex for the protocol. + * By default this will check for http and https + */ + $wrongProtocol = !preg_match(self::$_protocolRegex, $url); + $url = filter_var($url, FILTER_VALIDATE_URL); + if ($wrongProtocol || empty($url)) { + throw new Error( + 'Redirect to invalid URL: ' . $url, + Error::REDIRECT_INVALID_URL + ); + } + + /* Add encoded parameters */ + if (strpos($url, '?') === false) { + $paramPrefix = '?'; + } else { + $paramPrefix = '&'; + } + + foreach ($parameters as $name => $value) { + if ($value === null) { + $param = urlencode($name); + } else if (is_array($value)) { + $param = ""; + foreach ($value as $val) { + $param .= urlencode($name) . "[]=" . urlencode($val). '&'; + } + if (!empty($param)) { + $param = substr($param, 0, -1); + } + } else { + $param = urlencode($name) . '=' . urlencode($value); + } + + if (!empty($param)) { + $url .= $paramPrefix . $param; + $paramPrefix = '&'; + } + } + + if ($stay) { + return $url; + } + + header('Pragma: no-cache'); + header('Cache-Control: no-cache, must-revalidate'); + header('Location: ' . $url); + exit(); + } + + /** + * @param $protocolRegex string + */ + public static function setProtocolRegex($protocolRegex) + { + if (!empty($protocolRegex)) { + self::$_protocolRegex = $protocolRegex; + } + } + + /** + * Set the Base URL value. + * + * @param string $baseurl The base url to be used when constructing URLs + */ + public static function setBaseURL($baseurl) + { + if (!empty($baseurl)) { + $baseurlpath = '/'; + $matches = array(); + if (preg_match('#^https?://([^/]*)/?(.*)#i', $baseurl, $matches)) { + if (strpos($baseurl, 'https://') === false) { + self::setSelfProtocol('http'); + $port = '80'; + } else { + self::setSelfProtocol('https'); + $port = '443'; + } + + $currentHost = $matches[1]; + if (false !== strpos($currentHost, ':')) { + list($currentHost, $possiblePort) = explode(':', $matches[1], 2); + if (is_numeric($possiblePort)) { + $port = $possiblePort; + } + } + + if (isset($matches[2]) && !empty($matches[2])) { + $baseurlpath = $matches[2]; + } + + self::setSelfHost($currentHost); + self::setSelfPort($port); + self::setBaseURLPath($baseurlpath); + } + } else { + self::$_host = null; + self::$_protocol = null; + self::$_port = null; + self::$_baseurlpath = null; + } + } + + /** + * @param bool $proxyVars Whether to use `X-Forwarded-*` headers to determine port/domain/protocol + */ + public static function setProxyVars($proxyVars) + { + self::$_proxyVars = (bool)$proxyVars; + } + + /** + * @return bool + */ + public static function getProxyVars() + { + return self::$_proxyVars; + } + + /** + * Returns the protocol + the current host + the port (if different than + * common ports). + * + * @return string The URL + */ + public static function getSelfURLhost() + { + $currenthost = self::getSelfHost(); + + $port = ''; + + if (self::isHTTPS()) { + $protocol = 'https'; + } else { + $protocol = 'http'; + } + + $portnumber = self::getSelfPort(); + + if (isset($portnumber) && ($portnumber != '80') && ($portnumber != '443')) { + $port = ':' . $portnumber; + } + + return $protocol."://" . $currenthost . $port; + } + + /** + * @param string $host The host to use when constructing URLs + */ + public static function setSelfHost($host) + { + self::$_host = $host; + } + + /** + * @param string $baseurlpath The baseurl path to use when constructing URLs + */ + public static function setBaseURLPath($baseurlpath) + { + if (empty($baseurlpath)) { + self::$_baseurlpath = null; + } else if ($baseurlpath == '/') { + self::$_baseurlpath = '/'; + } else { + self::$_baseurlpath = '/' . trim($baseurlpath, '/') . '/'; + } + } + + /** + * @return string The baseurlpath to be used when constructing URLs + */ + public static function getBaseURLPath() + { + return self::$_baseurlpath; + } + + /** + * @return string The raw host name + */ + protected static function getRawHost() + { + if (self::$_host) { + $currentHost = self::$_host; + } elseif (self::getProxyVars() && array_key_exists('HTTP_X_FORWARDED_HOST', $_SERVER)) { + $currentHost = $_SERVER['HTTP_X_FORWARDED_HOST']; + } elseif (array_key_exists('HTTP_HOST', $_SERVER)) { + $currentHost = $_SERVER['HTTP_HOST']; + } elseif (array_key_exists('SERVER_NAME', $_SERVER)) { + $currentHost = $_SERVER['SERVER_NAME']; + } else { + if (function_exists('gethostname')) { + $currentHost = gethostname(); + } else { + $currentHost = php_uname("n"); + } + } + return $currentHost; + } + + /** + * @param int $port The port number to use when constructing URLs + */ + public static function setSelfPort($port) + { + self::$_port = $port; + } + + /** + * @param string $protocol The protocol to identify as using, usually http or https + */ + public static function setSelfProtocol($protocol) + { + self::$_protocol = $protocol; + } + + /** + * @return string http|https + */ + public static function getSelfProtocol() + { + $protocol = 'http'; + if (self::$_protocol) { + $protocol = self::$_protocol; + } elseif (self::getSelfPort() == 443) { + $protocol = 'https'; + } elseif (self::getProxyVars() && isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) { + $protocol = $_SERVER['HTTP_X_FORWARDED_PROTO']; + } elseif (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') { + $protocol = 'https'; + } + return $protocol; + } + + /** + * Returns the current host. + * + * @return string $currentHost The current host + */ + public static function getSelfHost() + { + $currentHost = self::getRawHost(); + + // strip the port + if (false !== strpos($currentHost, ':')) { + list($currentHost, $port) = explode(':', $currentHost, 2); + } + + return $currentHost; + } + + /** + * @return null|string The port number used for the request + */ + public static function getSelfPort() + { + $portnumber = null; + if (self::$_port) { + $portnumber = self::$_port; + } else if (self::getProxyVars() && isset($_SERVER["HTTP_X_FORWARDED_PORT"])) { + $portnumber = $_SERVER["HTTP_X_FORWARDED_PORT"]; + } else if (isset($_SERVER["SERVER_PORT"])) { + $portnumber = $_SERVER["SERVER_PORT"]; + } else { + $currentHost = self::getRawHost(); + + // strip the port + if (false !== strpos($currentHost, ':')) { + list($currentHost, $port) = explode(':', $currentHost, 2); + if (is_numeric($port)) { + $portnumber = $port; + } + } + } + return $portnumber; + } + + /** + * Checks if https or http. + * + * @return bool $isHttps False if https is not active + */ + public static function isHTTPS() + { + return self::getSelfProtocol() == 'https'; + } + + /** + * Returns the URL of the current host + current view. + * + * @return string + */ + public static function getSelfURLNoQuery() + { + $selfURLNoQuery = self::getSelfURLhost(); + + $infoWithBaseURLPath = self::buildWithBaseURLPath($_SERVER['SCRIPT_NAME']); + if (!empty($infoWithBaseURLPath)) { + $selfURLNoQuery .= $infoWithBaseURLPath; + } else { + $selfURLNoQuery .= $_SERVER['SCRIPT_NAME']; + } + + if (isset($_SERVER['PATH_INFO'])) { + $selfURLNoQuery .= $_SERVER['PATH_INFO']; + } + + return $selfURLNoQuery; + } + + /** + * Returns the routed URL of the current host + current view. + * + * @return string + */ + public static function getSelfRoutedURLNoQuery() + { + $selfURLhost = self::getSelfURLhost(); + $route = ''; + + if (!empty($_SERVER['REQUEST_URI'])) { + $route = $_SERVER['REQUEST_URI']; + if (!empty($_SERVER['QUERY_STRING'])) { + $route = str_replace($_SERVER['QUERY_STRING'], '', $route); + if (substr($route, -1) == '?') { + $route = substr($route, 0, -1); + } + } + } + + $infoWithBaseURLPath = self::buildWithBaseURLPath($route); + if (!empty($infoWithBaseURLPath)) { + $route = $infoWithBaseURLPath; + } + + $selfRoutedURLNoQuery = $selfURLhost . $route; + return $selfRoutedURLNoQuery; + } + + /** + * Returns the URL of the current host + current view + query. + * + * @return string + */ + public static function getSelfURL() + { + $selfURLhost = self::getSelfURLhost(); + + $requestURI = ''; + if (!empty($_SERVER['REQUEST_URI'])) { + $requestURI = $_SERVER['REQUEST_URI']; + $matches = array(); + if ($requestURI[0] !== '/' && preg_match('#^https?://[^/]*(/.*)#i', $requestURI, $matches)) { + $requestURI = $matches[1]; + } + } + + $infoWithBaseURLPath = self::buildWithBaseURLPath($requestURI); + if (!empty($infoWithBaseURLPath)) { + $requestURI = $infoWithBaseURLPath; + } + + return $selfURLhost . $requestURI; + } + + /** + * Returns the part of the URL with the BaseURLPath. + * + * @param string $info Contains path info + * + * @return string + */ + protected static function buildWithBaseURLPath($info) + { + $result = ''; + $baseURLPath = self::getBaseURLPath(); + if (!empty($baseURLPath)) { + $result = $baseURLPath; + if (!empty($info)) { + $path = explode('/', $info); + $extractedInfo = array_pop($path); + if (!empty($extractedInfo)) { + $result .= $extractedInfo; + } + } + } + return $result; + } + + /** + * Extract a query param - as it was sent - from $_SERVER[QUERY_STRING] + * + * @param string $name The param to-be extracted + * + * @return string + */ + public static function extractOriginalQueryParam($name) + { + $index = strpos($_SERVER['QUERY_STRING'], $name.'='); + $substring = substr($_SERVER['QUERY_STRING'], $index + strlen($name) + 1); + $end = strpos($substring, '&'); + return $end ? substr($substring, 0, strpos($substring, '&')) : $substring; + } + + /** + * Generates an unique string (used for example as ID for assertions). + * + * @return string A unique string + */ + public static function generateUniqueID() + { + return 'ONELOGIN_' . sha1(uniqid((string)mt_rand(), true)); + } + + /** + * Converts a UNIX timestamp to SAML2 timestamp on the form + * yyyy-mm-ddThh:mm:ss(\.s+)?Z. + * + * @param string|int $time The time we should convert (DateTime). + * + * @return string $timestamp SAML2 timestamp. + */ + public static function parseTime2SAML($time) + { + $date = new \DateTime("@$time", new \DateTimeZone('UTC')); + $timestamp = $date->format("Y-m-d\TH:i:s\Z"); + return $timestamp; + } + + /** + * Converts a SAML2 timestamp on the form yyyy-mm-ddThh:mm:ss(\.s+)?Z + * to a UNIX timestamp. The sub-second part is ignored. + * + * @param string $time The time we should convert (SAML Timestamp). + * + * @return int $timestamp Converted to a unix timestamp. + * + * @throws Exception + */ + public static function parseSAML2Time($time) + { + $matches = array(); + + /* We use a very strict regex to parse the timestamp. */ + $exp1 = '/^(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)'; + $exp2 = 'T(\\d\\d):(\\d\\d):(\\d\\d)(?:\\.\\d+)?Z$/D'; + if (preg_match($exp1 . $exp2, $time, $matches) == 0) { + throw new Exception( + 'Invalid SAML2 timestamp passed to' . + ' parseSAML2Time: ' . $time + ); + } + + /* Extract the different components of the time from the + * matches in the regex. int cast will ignore leading zeroes + * in the string. + */ + $year = (int) $matches[1]; + $month = (int) $matches[2]; + $day = (int) $matches[3]; + $hour = (int) $matches[4]; + $minute = (int) $matches[5]; + $second = (int) $matches[6]; + + /* We use gmmktime because the timestamp will always be given + * in UTC. + */ + $ts = gmmktime($hour, $minute, $second, $month, $day, $year); + + return $ts; + } + + + /** + * Interprets a ISO8601 duration value relative to a given timestamp. + * + * @param string $duration The duration, as a string. + * @param int|null $timestamp The unix timestamp we should apply the + * duration to. Optional, default to the + * current time. + * + * @return int The new timestamp, after the duration is applied. + * + * @throws Exception + */ + public static function parseDuration($duration, $timestamp = null) + { + assert(is_string($duration)); + assert(is_null($timestamp) || is_int($timestamp)); + + $matches = array(); + + /* Parse the duration. We use a very strict pattern. */ + $durationRegEx = '#^(-?)P(?:(?:(?:(\\d+)Y)?(?:(\\d+)M)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)S)?)?)|(?:(\\d+)W))$#D'; + if (!preg_match($durationRegEx, $duration, $matches)) { + throw new Exception('Invalid ISO 8601 duration: ' . $duration); + } + + $durYears = (empty($matches[2]) ? 0 : (int)$matches[2]); + $durMonths = (empty($matches[3]) ? 0 : (int)$matches[3]); + $durDays = (empty($matches[4]) ? 0 : (int)$matches[4]); + $durHours = (empty($matches[5]) ? 0 : (int)$matches[5]); + $durMinutes = (empty($matches[6]) ? 0 : (int)$matches[6]); + $durSeconds = (empty($matches[7]) ? 0 : (int)$matches[7]); + $durWeeks = (empty($matches[8]) ? 0 : (int)$matches[8]); + + if (!empty($matches[1])) { + /* Negative */ + $durYears = -$durYears; + $durMonths = -$durMonths; + $durDays = -$durDays; + $durHours = -$durHours; + $durMinutes = -$durMinutes; + $durSeconds = -$durSeconds; + $durWeeks = -$durWeeks; + } + + if ($timestamp === null) { + $timestamp = time(); + } + + if ($durYears !== 0 || $durMonths !== 0) { + /* Special handling of months and years, since they aren't a specific interval, but + * instead depend on the current time. + */ + + /* We need the year and month from the timestamp. Unfortunately, PHP doesn't have the + * gmtime function. Instead we use the gmdate function, and split the result. + */ + $yearmonth = explode(':', gmdate('Y:n', $timestamp)); + $year = (int)$yearmonth[0]; + $month = (int)$yearmonth[1]; + + /* Remove the year and month from the timestamp. */ + $timestamp -= gmmktime(0, 0, 0, $month, 1, $year); + + /* Add years and months, and normalize the numbers afterwards. */ + $year += $durYears; + $month += $durMonths; + while ($month > 12) { + $year += 1; + $month -= 12; + } + while ($month < 1) { + $year -= 1; + $month += 12; + } + + /* Add year and month back into timestamp. */ + $timestamp += gmmktime(0, 0, 0, $month, 1, $year); + } + + /* Add the other elements. */ + $timestamp += $durWeeks * 7 * 24 * 60 * 60; + $timestamp += $durDays * 24 * 60 * 60; + $timestamp += $durHours * 60 * 60; + $timestamp += $durMinutes * 60; + $timestamp += $durSeconds; + + return $timestamp; + } + + /** + * Compares 2 dates and returns the earliest. + * + * @param string|null $cacheDuration The duration, as a string. + * @param string|int|null $validUntil The valid until date, as a string or as a timestamp + * + * @return int|null $expireTime The expiration time. + * + * @throws Exception + */ + public static function getExpireTime($cacheDuration = null, $validUntil = null) + { + $expireTime = null; + + if ($cacheDuration !== null) { + $expireTime = self::parseDuration($cacheDuration, time()); + } + + if ($validUntil !== null) { + if (is_int($validUntil)) { + $validUntilTime = $validUntil; + } else { + $validUntilTime = self::parseSAML2Time($validUntil); + } + if ($expireTime === null || $expireTime > $validUntilTime) { + $expireTime = $validUntilTime; + } + } + + return $expireTime; + } + + + /** + * Extracts nodes from the DOMDocument. + * + * @param DOMDocument $dom The DOMDocument + * @param string $query \Xpath Expression + * @param DOMElement|null $context Context Node (DOMElement) + * + * @return DOMNodeList The queried nodes + */ + public static function query(DOMDocument $dom, $query, DOMElement $context = null) + { + $xpath = new DOMXPath($dom); + $xpath->registerNamespace('samlp', Constants::NS_SAMLP); + $xpath->registerNamespace('saml', Constants::NS_SAML); + $xpath->registerNamespace('ds', Constants::NS_DS); + $xpath->registerNamespace('xenc', Constants::NS_XENC); + $xpath->registerNamespace('xsi', Constants::NS_XSI); + $xpath->registerNamespace('xs', Constants::NS_XS); + $xpath->registerNamespace('md', Constants::NS_MD); + + if (isset($context)) { + $res = $xpath->query($query, $context); + } else { + $res = $xpath->query($query); + } + return $res; + } + + /** + * Checks if the session is started or not. + * + * @return bool true if the sessíon is started + */ + public static function isSessionStarted() + { + if (PHP_VERSION_ID >= 50400) { + return session_status() === PHP_SESSION_ACTIVE ? true : false; + } else { + return session_id() === '' ? false : true; + } + } + + /** + * Deletes the local session. + */ + public static function deleteLocalSession() + { + + if (Utils::isSessionStarted()) { + session_destroy(); + } + + unset($_SESSION); + } + + /** + * Calculates the fingerprint of a x509cert. + * + * @param string $x509cert x509 cert formatted + * @param string $alg Algorithm to be used in order to calculate the fingerprint + * + * @return null|string Formatted fingerprint + */ + public static function calculateX509Fingerprint($x509cert, $alg = 'sha1') + { + assert(is_string($x509cert)); + + $arCert = explode("\n", $x509cert); + $data = ''; + $inData = false; + + foreach ($arCert as $curData) { + if (! $inData) { + if (strncmp($curData, '-----BEGIN CERTIFICATE', 22) == 0) { + $inData = true; + } elseif ((strncmp($curData, '-----BEGIN PUBLIC KEY', 21) == 0) || (strncmp($curData, '-----BEGIN RSA PRIVATE KEY', 26) == 0)) { + /* This isn't an X509 certificate. */ + return null; + } + } else { + if (strncmp($curData, '-----END CERTIFICATE', 20) == 0) { + break; + } + $data .= trim($curData); + } + } + + if (empty($data)) { + return null; + } + + $decodedData = base64_decode($data); + + switch ($alg) { + case 'sha512': + case 'sha384': + case 'sha256': + $fingerprint = hash($alg, $decodedData, false); + break; + case 'sha1': + default: + $fingerprint = strtolower(sha1($decodedData)); + break; + } + return $fingerprint; + } + + /** + * Formates a fingerprint. + * + * @param string $fingerprint fingerprint + * + * @return string Formatted fingerprint + */ + public static function formatFingerPrint($fingerprint) + { + $formatedFingerprint = str_replace(':', '', $fingerprint); + $formatedFingerprint = strtolower($formatedFingerprint); + return $formatedFingerprint; + } + + /** + * Generates a nameID. + * + * @param string $value fingerprint + * @param string $spnq SP Name Qualifier + * @param string|null $format SP Format + * @param string|null $cert IdP Public cert to encrypt the nameID + * @param string|null $nq IdP Name Qualifier + * + * @return string $nameIDElement DOMElement | XMLSec nameID + * + * @throws Exception + */ + public static function generateNameId($value, $spnq, $format = null, $cert = null, $nq = null) + { + + $doc = new DOMDocument(); + + $nameId = $doc->createElement('saml:NameID'); + if (isset($spnq)) { + $nameId->setAttribute('SPNameQualifier', $spnq); + } + if (isset($nq)) { + $nameId->setAttribute('NameQualifier', $nq); + } + if (isset($format)) { + $nameId->setAttribute('Format', $format); + } + $nameId->appendChild($doc->createTextNode($value)); + + $doc->appendChild($nameId); + + if (!empty($cert)) { + $seckey = new XMLSecurityKey(XMLSecurityKey::RSA_1_5, array('type'=>'public')); + $seckey->loadKey($cert); + + $enc = new XMLSecEnc(); + $enc->setNode($nameId); + $enc->type = XMLSecEnc::Element; + + $symmetricKey = new XMLSecurityKey(XMLSecurityKey::AES128_CBC); + $symmetricKey->generateSessionKey(); + $enc->encryptKey($seckey, $symmetricKey); + + $encryptedData = $enc->encryptNode($symmetricKey); + + $newdoc = new DOMDocument(); + + $encryptedID = $newdoc->createElement('saml:EncryptedID'); + + $newdoc->appendChild($encryptedID); + + $encryptedID->appendChild($encryptedID->ownerDocument->importNode($encryptedData, true)); + + return $newdoc->saveXML($encryptedID); + } else { + return $doc->saveXML($nameId); + } + } + + + /** + * Gets Status from a Response. + * + * @param DOMDocument $dom The Response as XML + * + * @return array $status The Status, an array with the code and a message. + * + * @throws ValidationError + */ + public static function getStatus(DOMDocument $dom) + { + $status = array(); + + $statusEntry = self::query($dom, '/samlp:Response/samlp:Status'); + if ($statusEntry->length != 1) { + throw new ValidationError( + "Missing Status on response", + ValidationError::MISSING_STATUS + ); + } + + $codeEntry = self::query($dom, '/samlp:Response/samlp:Status/samlp:StatusCode', $statusEntry->item(0)); + if ($codeEntry->length != 1) { + throw new ValidationError( + "Missing Status Code on response", + ValidationError::MISSING_STATUS_CODE + ); + } + $code = $codeEntry->item(0)->getAttribute('Value'); + $status['code'] = $code; + + $status['msg'] = ''; + $messageEntry = self::query($dom, '/samlp:Response/samlp:Status/samlp:StatusMessage', $statusEntry->item(0)); + if ($messageEntry->length == 0) { + $subCodeEntry = self::query($dom, '/samlp:Response/samlp:Status/samlp:StatusCode/samlp:StatusCode', $statusEntry->item(0)); + if ($subCodeEntry->length == 1) { + $status['msg'] = $subCodeEntry->item(0)->getAttribute('Value'); + } + } else if ($messageEntry->length == 1) { + $msg = $messageEntry->item(0)->textContent; + $status['msg'] = $msg; + } + + return $status; + } + + /** + * Decrypts an encrypted element. + * + * @param DOMElement $encryptedData The encrypted data. + * @param XMLSecurityKey $inputKey The decryption key. + * @param bool $formatOutput Format or not the output. + * + * @return DOMElement The decrypted element. + * + * @throws ValidationError + */ + public static function decryptElement(DOMElement $encryptedData, XMLSecurityKey $inputKey, $formatOutput = true) + { + + $enc = new XMLSecEnc(); + + $enc->setNode($encryptedData); + $enc->type = $encryptedData->getAttribute("Type"); + + $symmetricKey = $enc->locateKey($encryptedData); + if (!$symmetricKey) { + throw new ValidationError( + 'Could not locate key algorithm in encrypted data.', + ValidationError::KEY_ALGORITHM_ERROR + ); + } + + $symmetricKeyInfo = $enc->locateKeyInfo($symmetricKey); + if (!$symmetricKeyInfo) { + throw new ValidationError( + "Could not locate for the encrypted key.", + ValidationError::KEYINFO_NOT_FOUND_IN_ENCRYPTED_DATA + ); + } + + $inputKeyAlgo = $inputKey->getAlgorithm(); + if ($symmetricKeyInfo->isEncrypted) { + $symKeyInfoAlgo = $symmetricKeyInfo->getAlgorithm(); + + if ($symKeyInfoAlgo === XMLSecurityKey::RSA_OAEP_MGF1P && $inputKeyAlgo === XMLSecurityKey::RSA_1_5) { + $inputKeyAlgo = XMLSecurityKey::RSA_OAEP_MGF1P; + } + + if ($inputKeyAlgo !== $symKeyInfoAlgo) { + throw new ValidationError( + 'Algorithm mismatch between input key and key used to encrypt ' . + ' the symmetric key for the message. Key was: ' . + var_export($inputKeyAlgo, true) . '; message was: ' . + var_export($symKeyInfoAlgo, true), + ValidationError::KEY_ALGORITHM_ERROR + ); + } + + $encKey = $symmetricKeyInfo->encryptedCtx; + $symmetricKeyInfo->key = $inputKey->key; + $keySize = $symmetricKey->getSymmetricKeySize(); + if ($keySize === null) { + // To protect against "key oracle" attacks + throw new ValidationError( + 'Unknown key size for encryption algorithm: ' . var_export($symmetricKey->type, true), + ValidationError::KEY_ALGORITHM_ERROR + ); + } + + $key = $encKey->decryptKey($symmetricKeyInfo); + if (strlen($key) != $keySize) { + $encryptedKey = $encKey->getCipherValue(); + $pkey = openssl_pkey_get_details($symmetricKeyInfo->key); + $pkey = sha1(serialize($pkey), true); + $key = sha1($encryptedKey . $pkey, true); + + /* Make sure that the key has the correct length. */ + if (strlen($key) > $keySize) { + $key = substr($key, 0, $keySize); + } elseif (strlen($key) < $keySize) { + $key = str_pad($key, $keySize); + } + } + $symmetricKey->loadKey($key); + } else { + $symKeyAlgo = $symmetricKey->getAlgorithm(); + if ($inputKeyAlgo !== $symKeyAlgo) { + throw new ValidationError( + 'Algorithm mismatch between input key and key in message. ' . + 'Key was: ' . var_export($inputKeyAlgo, true) . '; message was: ' . + var_export($symKeyAlgo, true), + ValidationError::KEY_ALGORITHM_ERROR + ); + } + $symmetricKey = $inputKey; + } + + $decrypted = $enc->decryptNode($symmetricKey, false); + + $xml = ''.$decrypted.''; + $newDoc = new DOMDocument(); + if ($formatOutput) { + $newDoc->preserveWhiteSpace = false; + $newDoc->formatOutput = true; + } + $newDoc = self::loadXML($newDoc, $xml); + if (!$newDoc) { + throw new ValidationError( + 'Failed to parse decrypted XML.', + ValidationError::INVALID_XML_FORMAT + ); + } + + $decryptedElement = $newDoc->firstChild->firstChild; + if ($decryptedElement === null) { + throw new ValidationError( + 'Missing encrypted element.', + ValidationError::MISSING_ENCRYPTED_ELEMENT + ); + } + + return $decryptedElement; + } + + /** + * Converts a XMLSecurityKey to the correct algorithm. + * + * @param XMLSecurityKey $key The key. + * @param string $algorithm The desired algorithm. + * @param string $type Public or private key, defaults to public. + * + * @return XMLSecurityKey The new key. + * + * @throws Exception + */ + public static function castKey(XMLSecurityKey $key, $algorithm, $type = 'public') + { + assert(is_string($algorithm)); + assert($type === 'public' || $type === 'private'); + + // do nothing if algorithm is already the type of the key + if ($key->type === $algorithm) { + return $key; + } + + if (!Utils::isSupportedSigningAlgorithm($algorithm)) { + throw new Exception('Unsupported signing algorithm.'); + } + + $keyInfo = openssl_pkey_get_details($key->key); + if ($keyInfo === false) { + throw new Exception('Unable to get key details from XMLSecurityKey.'); + } + if (!isset($keyInfo['key'])) { + throw new Exception('Missing key in public key details.'); + } + $newKey = new XMLSecurityKey($algorithm, array('type'=>$type)); + $newKey->loadKey($keyInfo['key']); + return $newKey; + } + + /** + * @param $algorithm + * + * @return bool + */ + public static function isSupportedSigningAlgorithm($algorithm) + { + return in_array( + $algorithm, + array( + XMLSecurityKey::RSA_1_5, + XMLSecurityKey::RSA_SHA1, + XMLSecurityKey::RSA_SHA256, + XMLSecurityKey::RSA_SHA384, + XMLSecurityKey::RSA_SHA512 + ) + ); + } + + /** + * Adds signature key and senders certificate to an element (Message or Assertion). + * + * @param string|DOMDocument $xml The element we should sign + * @param string $key The private key + * @param string $cert The public + * @param string $signAlgorithm Signature algorithm method + * @param string $digestAlgorithm Digest algorithm method + * + * @return string + * + * @throws Exception + */ + public static function addSign($xml, $key, $cert, $signAlgorithm = XMLSecurityKey::RSA_SHA256, $digestAlgorithm = XMLSecurityDSig::SHA256) + { + if ($xml instanceof DOMDocument) { + $dom = $xml; + } else { + $dom = new DOMDocument(); + $dom = self::loadXML($dom, $xml); + if (!$dom) { + throw new Exception('Error parsing xml string'); + } + } + + /* Load the private key. */ + $objKey = new XMLSecurityKey($signAlgorithm, array('type' => 'private')); + $objKey->loadKey($key, false); + + /* Get the EntityDescriptor node we should sign. */ + $rootNode = $dom->firstChild; + + /* Sign the metadata with our private key. */ + $objXMLSecDSig = new XMLSecurityDSig(); + $objXMLSecDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N); + + $objXMLSecDSig->addReferenceList( + array($rootNode), + $digestAlgorithm, + array('http://www.w3.org/2000/09/xmldsig#enveloped-signature', XMLSecurityDSig::EXC_C14N), + array('id_name' => 'ID') + ); + + $objXMLSecDSig->sign($objKey); + + /* Add the certificate to the signature. */ + $objXMLSecDSig->add509Cert($cert, true); + + $insertBefore = $rootNode->firstChild; + $messageTypes = array('AuthnRequest', 'Response', 'LogoutRequest','LogoutResponse'); + if (in_array($rootNode->localName, $messageTypes)) { + $issuerNodes = self::query($dom, '/'.$rootNode->tagName.'/saml:Issuer'); + if ($issuerNodes->length == 1) { + $insertBefore = $issuerNodes->item(0)->nextSibling; + } + } + + /* Add the signature. */ + $objXMLSecDSig->insertSignature($rootNode, $insertBefore); + + /* Return the DOM tree as a string. */ + $signedxml = $dom->saveXML(); + + return $signedxml; + } + + /** + * Validates a signature (Message or Assertion). + * + * @param string|\DomNode $xml The element we should validate + * @param string|null $cert The pubic cert + * @param string|null $fingerprint The fingerprint of the public cert + * @param string|null $fingerprintalg The algorithm used to get the fingerprint + * @param string|null $xpath The xpath of the signed element + * @param array|null $multiCerts Multiple public certs + * + * @return bool + * + * @throws Exception + */ + public static function validateSign($xml, $cert = null, $fingerprint = null, $fingerprintalg = 'sha1', $xpath = null, $multiCerts = null) + { + if ($xml instanceof DOMDocument) { + $dom = clone $xml; + } else if ($xml instanceof DOMElement) { + $dom = clone $xml->ownerDocument; + } else { + $dom = new DOMDocument(); + $dom = self::loadXML($dom, $xml); + } + + $objXMLSecDSig = new XMLSecurityDSig(); + $objXMLSecDSig->idKeys = array('ID'); + + if ($xpath) { + $nodeset = Utils::query($dom, $xpath); + $objDSig = $nodeset->item(0); + $objXMLSecDSig->sigNode = $objDSig; + } else { + $objDSig = $objXMLSecDSig->locateSignature($dom); + } + + if (!$objDSig) { + throw new Exception('Cannot locate Signature Node'); + } + + $objKey = $objXMLSecDSig->locateKey(); + if (!$objKey) { + throw new Exception('We have no idea about the key'); + } + + if (!Utils::isSupportedSigningAlgorithm($objKey->type)) { + throw new Exception('Unsupported signing algorithm.'); + } + + $objXMLSecDSig->canonicalizeSignedInfo(); + + try { + $retVal = $objXMLSecDSig->validateReference(); + } catch (Exception $e) { + throw $e; + } + + XMLSecEnc::staticLocateKeyInfo($objKey, $objDSig); + + if (!empty($multiCerts)) { + // If multiple certs are provided, I may ignore $cert and + // $fingerprint provided by the method and just check the + // certs on the array + $fingerprint = null; + } else { + // else I add the cert to the array in order to check + // validate signatures with it and the with it and the + // $fingerprint value + $multiCerts = array($cert); + } + + $valid = false; + foreach ($multiCerts as $cert) { + if (!empty($cert)) { + $objKey->loadKey($cert, false, true); + if ($objXMLSecDSig->verify($objKey) === 1) { + $valid = true; + break; + } + } else { + if (!empty($fingerprint)) { + $domCert = $objKey->getX509Certificate(); + $domCertFingerprint = Utils::calculateX509Fingerprint($domCert, $fingerprintalg); + if (Utils::formatFingerPrint($fingerprint) == $domCertFingerprint) { + $objKey->loadKey($domCert, false, true); + if ($objXMLSecDSig->verify($objKey) === 1) { + $valid = true; + break; + } + } + } + } + } + return $valid; + } + + /** + * Validates a binary signature + * + * @param string $messageType Type of SAML Message + * @param array $getData HTTP GET array + * @param array $idpData IdP setting data + * @param bool $retrieveParametersFromServer Indicates where to get the values in order to validate the Sign, from getData or from $_SERVER + * + * @return bool + * + * @throws Exception + */ + public static function validateBinarySign($messageType, $getData, $idpData, $retrieveParametersFromServer = false) + { + if (!isset($getData['SigAlg'])) { + $signAlg = XMLSecurityKey::RSA_SHA1; + } else { + $signAlg = $getData['SigAlg']; + } + + if ($retrieveParametersFromServer) { + $signedQuery = $messageType.'='.Utils::extractOriginalQueryParam($messageType); + if (isset($getData['RelayState'])) { + $signedQuery .= '&RelayState='.Utils::extractOriginalQueryParam('RelayState'); + } + $signedQuery .= '&SigAlg='.Utils::extractOriginalQueryParam('SigAlg'); + } else { + $signedQuery = $messageType.'='.urlencode($getData[$messageType]); + if (isset($getData['RelayState'])) { + $signedQuery .= '&RelayState='.urlencode($getData['RelayState']); + } + $signedQuery .= '&SigAlg='.urlencode($signAlg); + } + + if ($messageType == "SAMLRequest") { + $strMessageType = "Logout Request"; + } else { + $strMessageType = "Logout Response"; + } + $existsMultiX509Sign = isset($idpData['x509certMulti']) && isset($idpData['x509certMulti']['signing']) && !empty($idpData['x509certMulti']['signing']); + if ((!isset($idpData['x509cert']) || empty($idpData['x509cert'])) && !$existsMultiX509Sign) { + throw new Error( + "In order to validate the sign on the ".$strMessageType.", the x509cert of the IdP is required", + Error::CERT_NOT_FOUND + ); + } + + if ($existsMultiX509Sign) { + $multiCerts = $idpData['x509certMulti']['signing']; + } else { + $multiCerts = array($idpData['x509cert']); + } + + $signatureValid = false; + foreach ($multiCerts as $cert) { + $objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'public')); + $objKey->loadKey($cert, false, true); + + if ($signAlg != XMLSecurityKey::RSA_SHA1) { + try { + $objKey = Utils::castKey($objKey, $signAlg, 'public'); + } catch (Exception $e) { + $ex = new ValidationError( + "Invalid signAlg in the recieved ".$strMessageType, + ValidationError::INVALID_SIGNATURE + ); + if (count($multiCerts) == 1) { + throw $ex; + } + } + } + + if ($objKey->verifySignature($signedQuery, base64_decode($_GET['Signature'])) === 1) { + $signatureValid = true; + break; + } + } + return $signatureValid; + } +} diff --git a/vendor/onelogin/php-saml/src/Saml2/ValidationError.php b/vendor/onelogin/php-saml/src/Saml2/ValidationError.php new file mode 100644 index 0000000000..889f531cab --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/ValidationError.php @@ -0,0 +1,100 @@ + + * @license MIT https://github.com/onelogin/php-saml/blob/master/LICENSE + * @link https://github.com/onelogin/php-saml + */ + +namespace OneLogin\Saml2; + +use Exception; + +/** + * ValidationError class of OneLogin PHP Toolkit + * + * This class implements another custom Exception handler, + * related to exceptions that happens during validation process. + */ +class ValidationError extends Exception +{ + // Validation Errors + const UNSUPPORTED_SAML_VERSION = 0; + const MISSING_ID = 1; + const WRONG_NUMBER_OF_ASSERTIONS = 2; + const MISSING_STATUS = 3; + const MISSING_STATUS_CODE = 4; + const STATUS_CODE_IS_NOT_SUCCESS = 5; + const WRONG_SIGNED_ELEMENT = 6; + const ID_NOT_FOUND_IN_SIGNED_ELEMENT = 7; + const DUPLICATED_ID_IN_SIGNED_ELEMENTS = 8; + const INVALID_SIGNED_ELEMENT = 9; + const DUPLICATED_REFERENCE_IN_SIGNED_ELEMENTS = 10; + const UNEXPECTED_SIGNED_ELEMENTS = 11; + const WRONG_NUMBER_OF_SIGNATURES_IN_RESPONSE = 12; + const WRONG_NUMBER_OF_SIGNATURES_IN_ASSERTION = 13; + const INVALID_XML_FORMAT = 14; + const WRONG_INRESPONSETO = 15; + const NO_ENCRYPTED_ASSERTION = 16; + const NO_ENCRYPTED_NAMEID = 17; + const MISSING_CONDITIONS = 18; + const ASSERTION_TOO_EARLY = 19; + const ASSERTION_EXPIRED = 20; + const WRONG_NUMBER_OF_AUTHSTATEMENTS = 21; + const NO_ATTRIBUTESTATEMENT = 22; + const ENCRYPTED_ATTRIBUTES = 23; + const WRONG_DESTINATION = 24; + const EMPTY_DESTINATION = 25; + const WRONG_AUDIENCE = 26; + const ISSUER_MULTIPLE_IN_RESPONSE = 27; + const ISSUER_NOT_FOUND_IN_ASSERTION = 28; + const WRONG_ISSUER = 29; + const SESSION_EXPIRED = 30; + const WRONG_SUBJECTCONFIRMATION = 31; + const NO_SIGNED_MESSAGE = 32; + const NO_SIGNED_ASSERTION = 33; + const NO_SIGNATURE_FOUND = 34; + const KEYINFO_NOT_FOUND_IN_ENCRYPTED_DATA = 35; + const CHILDREN_NODE_NOT_FOUND_IN_KEYINFO = 36; + const UNSUPPORTED_RETRIEVAL_METHOD = 37; + const NO_NAMEID = 38; + const EMPTY_NAMEID = 39; + const SP_NAME_QUALIFIER_NAME_MISMATCH = 40; + const DUPLICATED_ATTRIBUTE_NAME_FOUND = 41; + const INVALID_SIGNATURE = 42; + const WRONG_NUMBER_OF_SIGNATURES = 43; + const RESPONSE_EXPIRED = 44; + const UNEXPECTED_REFERENCE = 45; + const NOT_SUPPORTED = 46; + const KEY_ALGORITHM_ERROR = 47; + const MISSING_ENCRYPTED_ELEMENT = 48; + + + /** + * Constructor + * + * @param string $msg Describes the error. + * @param int $code The code error (defined in the error class). + * @param array|null $args Arguments used in the message that describes the error. + */ + public function __construct($msg, $code = 0, $args = array()) + { + assert(is_string($msg)); + assert(is_int($code)); + + if (!isset($args)) { + $args = array(); + } + $params = array_merge(array($msg), $args); + $message = call_user_func_array('sprintf', $params); + + parent::__construct($message, $code); + } +} diff --git a/vendor/onelogin/php-saml/src/Saml2/schemas/saml-schema-assertion-2.0.xsd b/vendor/onelogin/php-saml/src/Saml2/schemas/saml-schema-assertion-2.0.xsd new file mode 100644 index 0000000000..2b2f7b8018 --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/schemas/saml-schema-assertion-2.0.xsd @@ -0,0 +1,283 @@ + + + + + + + Document identifier: saml-schema-assertion-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V1.0 (November, 2002): + Initial Standard Schema. + V1.1 (September, 2003): + Updates within the same V1.0 namespace. + V2.0 (March, 2005): + New assertion schema for SAML V2.0 namespace. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/onelogin/php-saml/src/Saml2/schemas/saml-schema-authn-context-2.0.xsd b/vendor/onelogin/php-saml/src/Saml2/schemas/saml-schema-authn-context-2.0.xsd new file mode 100644 index 0000000000..e4754faf8c --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/schemas/saml-schema-authn-context-2.0.xsd @@ -0,0 +1,23 @@ + + + + + + Document identifier: saml-schema-authn-context-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New core authentication context schema for SAML V2.0. + This is just an include of all types from the schema + referred to in the include statement below. + + + + + + \ No newline at end of file diff --git a/vendor/onelogin/php-saml/src/Saml2/schemas/saml-schema-authn-context-types-2.0.xsd b/vendor/onelogin/php-saml/src/Saml2/schemas/saml-schema-authn-context-types-2.0.xsd new file mode 100644 index 0000000000..8513959a5d --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/schemas/saml-schema-authn-context-types-2.0.xsd @@ -0,0 +1,821 @@ + + + + + + Document identifier: saml-schema-authn-context-types-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + New core authentication context schema types for SAML V2.0. + + + + + + + A particular assertion on an identity + provider's part with respect to the authentication + context associated with an authentication assertion. + + + + + + + + Refers to those characteristics that describe the + processes and mechanisms + the Authentication Authority uses to initially create + an association between a Principal + and the identity (or name) by which the Principal will + be known + + + + + + + + This element indicates that identification has been + performed in a physical + face-to-face meeting with the principal and not in an + online manner. + + + + + + + + + + + + + + + + + + + + Refers to those characterstics that describe how the + 'secret' (the knowledge or possession + of which allows the Principal to authenticate to the + Authentication Authority) is kept secure + + + + + + + + This element indicates the types and strengths of + facilities + of a UA used to protect a shared secret key from + unauthorized access and/or use. + + + + + + + + This element indicates the types and strengths of + facilities + of a UA used to protect a private key from + unauthorized access and/or use. + + + + + + + The actions that must be performed + before the private key can be used. + + + + + + Whether or not the private key is shared + with the certificate authority. + + + + + + + In which medium is the key stored. + memory - the key is stored in memory. + smartcard - the key is stored in a smartcard. + token - the key is stored in a hardware token. + MobileDevice - the key is stored in a mobile device. + MobileAuthCard - the key is stored in a mobile + authentication card. + + + + + + + + + + + This element indicates that a password (or passphrase) + has been used to + authenticate the Principal to a remote system. + + + + + + + + This element indicates that a Pin (Personal + Identification Number) has been used to authenticate the Principal to + some local system in order to activate a key. + + + + + + + + This element indicates that a hardware or software + token is used + as a method of identifying the Principal. + + + + + + + + This element indicates that a time synchronization + token is used to identify the Principal. hardware - + the time synchonization + token has been implemented in hardware. software - the + time synchronization + token has been implemented in software. SeedLength - + the length, in bits, of the + random seed used in the time synchronization token. + + + + + + + + This element indicates that a smartcard is used to + identity the Principal. + + + + + + + + This element indicates the minimum and/or maximum + ASCII length of the password which is enforced (by the UA or the + IdP). In other words, this is the minimum and/or maximum number of + ASCII characters required to represent a valid password. + min - the minimum number of ASCII characters required + in a valid password, as enforced by the UA or the IdP. + max - the maximum number of ASCII characters required + in a valid password, as enforced by the UA or the IdP. + + + + + + + + This element indicates the length of time for which an + PIN-based authentication is valid. + + + + + + + + Indicates whether the password was chosen by the + Principal or auto-supplied by the Authentication Authority. + principalchosen - the Principal is allowed to choose + the value of the password. This is true even if + the initial password is chosen at random by the UA or + the IdP and the Principal is then free to change + the password. + automatic - the password is chosen by the UA or the + IdP to be cryptographically strong in some sense, + or to satisfy certain password rules, and that the + Principal is not free to change it or to choose a new password. + + + + + + + + + + + + + + + + + + + Refers to those characteristics that define the + mechanisms by which the Principal authenticates to the Authentication + Authority. + + + + + + + + The method that a Principal employs to perform + authentication to local system components. + + + + + + + + The method applied to validate a principal's + authentication across a network + + + + + + + + Supports Authenticators with nested combinations of + additional complexity. + + + + + + + + Indicates that the Principal has been strongly + authenticated in a previous session during which the IdP has set a + cookie in the UA. During the present session the Principal has only + been authenticated by the UA returning the cookie to the IdP. + + + + + + + + Rather like PreviousSession but using stronger + security. A secret that was established in a previous session with + the Authentication Authority has been cached by the local system and + is now re-used (e.g. a Master Secret is used to derive new session + keys in TLS, SSL, WTLS). + + + + + + + + This element indicates that the Principal has been + authenticated by a zero knowledge technique as specified in ISO/IEC + 9798-5. + + + + + + + + + + This element indicates that the Principal has been + authenticated by a challenge-response protocol utilizing shared secret + keys and symmetric cryptography. + + + + + + + + + + + + This element indicates that the Principal has been + authenticated by a mechanism which involves the Principal computing a + digital signature over at least challenge data provided by the IdP. + + + + + + + + The local system has a private key but it is used + in decryption mode, rather than signature mode. For example, the + Authentication Authority generates a secret and encrypts it using the + local system's public key: the local system then proves it has + decrypted the secret. + + + + + + + + The local system has a private key and uses it for + shared secret key agreement with the Authentication Authority (e.g. + via Diffie Helman). + + + + + + + + + + + + + + + This element indicates that the Principal has been + authenticated through connection from a particular IP address. + + + + + + + + The local system and Authentication Authority + share a secret key. The local system uses this to encrypt a + randomised string to pass to the Authentication Authority. + + + + + + + + The protocol across which Authenticator information is + transferred to an Authentication Authority verifier. + + + + + + + + This element indicates that the Authenticator has been + transmitted using bare HTTP utilizing no additional security + protocols. + + + + + + + + This element indicates that the Authenticator has been + transmitted using a transport mechanism protected by an IPSEC session. + + + + + + + + This element indicates that the Authenticator has been + transmitted using a transport mechanism protected by a WTLS session. + + + + + + + + This element indicates that the Authenticator has been + transmitted solely across a mobile network using no additional + security mechanism. + + + + + + + + + + + This element indicates that the Authenticator has been + transmitted using a transport mechnanism protected by an SSL or TLS + session. + + + + + + + + + + + + Refers to those characteristics that describe + procedural security controls employed by the Authentication Authority. + + + + + + + + + + + + Provides a mechanism for linking to external (likely + human readable) documents in which additional business agreements, + (e.g. liability constraints, obligations, etc) can be placed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This attribute indicates whether or not the + Identification mechanisms allow the actions of the Principal to be + linked to an actual end user. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This element indicates that the Key Activation Limit is + defined as a specific duration of time. + + + + + + + + This element indicates that the Key Activation Limit is + defined as a number of usages. + + + + + + + + This element indicates that the Key Activation Limit is + the session. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/onelogin/php-saml/src/Saml2/schemas/saml-schema-metadata-2.0.xsd b/vendor/onelogin/php-saml/src/Saml2/schemas/saml-schema-metadata-2.0.xsd new file mode 100644 index 0000000000..86e58f9bb5 --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/schemas/saml-schema-metadata-2.0.xsd @@ -0,0 +1,336 @@ + + + + + + + + + Document identifier: saml-schema-metadata-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + Schema for SAML metadata, first published in SAML 2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/onelogin/php-saml/src/Saml2/schemas/saml-schema-protocol-2.0.xsd b/vendor/onelogin/php-saml/src/Saml2/schemas/saml-schema-protocol-2.0.xsd new file mode 100644 index 0000000000..7fa6f489d6 --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/schemas/saml-schema-protocol-2.0.xsd @@ -0,0 +1,302 @@ + + + + + + + Document identifier: saml-schema-protocol-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V1.0 (November, 2002): + Initial Standard Schema. + V1.1 (September, 2003): + Updates within the same V1.0 namespace. + V2.0 (March, 2005): + New protocol schema based in a SAML V2.0 namespace. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/onelogin/php-saml/src/Saml2/schemas/sstc-metadata-attr.xsd b/vendor/onelogin/php-saml/src/Saml2/schemas/sstc-metadata-attr.xsd new file mode 100644 index 0000000000..f23e462a5b --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/schemas/sstc-metadata-attr.xsd @@ -0,0 +1,35 @@ + + + + + + Document title: SAML V2.0 Metadata Extention for Entity Attributes Schema + Document identifier: sstc-metadata-attr.xsd + Location: http://www.oasis-open.org/committees/documents.php?wg_abbrev=security + Revision history: + V1.0 (November 2008): + Initial version. + + + + + + + + + + + + + + + diff --git a/vendor/onelogin/php-saml/src/Saml2/schemas/sstc-saml-attribute-ext.xsd b/vendor/onelogin/php-saml/src/Saml2/schemas/sstc-saml-attribute-ext.xsd new file mode 100644 index 0000000000..ad309c14b2 --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/schemas/sstc-saml-attribute-ext.xsd @@ -0,0 +1,25 @@ + + + + + + Document title: SAML V2.0 Attribute Extension Schema + Document identifier: sstc-saml-attribute-ext.xsd + Location: http://www.oasis-open.org/committees/documents.php?wg_abbrev=security + Revision history: + V1.0 (October 2008): + Initial version. + + + + + + + + diff --git a/vendor/onelogin/php-saml/src/Saml2/schemas/sstc-saml-metadata-algsupport-v1.0.xsd b/vendor/onelogin/php-saml/src/Saml2/schemas/sstc-saml-metadata-algsupport-v1.0.xsd new file mode 100644 index 0000000000..3236ffcdce --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/schemas/sstc-saml-metadata-algsupport-v1.0.xsd @@ -0,0 +1,41 @@ + + + + + + Document title: Metadata Extension Schema for SAML V2.0 Metadata Profile for Algorithm Support Version 1.0 + Document identifier: sstc-saml-metadata-algsupport.xsd + Location: http://docs.oasis-open.org/security/saml/Post2.0/ + Revision history: + V1.0 (June 2010): + Initial version. + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/onelogin/php-saml/src/Saml2/schemas/sstc-saml-metadata-ui-v1.0.xsd b/vendor/onelogin/php-saml/src/Saml2/schemas/sstc-saml-metadata-ui-v1.0.xsd new file mode 100644 index 0000000000..de0b754ab8 --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/schemas/sstc-saml-metadata-ui-v1.0.xsd @@ -0,0 +1,89 @@ + + + + + + Document title: Metadata Extension Schema for SAML V2.0 Metadata Extensions for Login and Discovery User Interface Version 1.0 + Document identifier: sstc-saml-metadata-ui-v1.0.xsd + Location: http://docs.oasis-open.org/security/saml/Post2.0/ + Revision history: + 16 November 2010: + Added Keywords element/type. + 01 November 2010 + Changed filename. + September 2010: + Initial version. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/onelogin/php-saml/src/Saml2/schemas/xenc-schema.xsd b/vendor/onelogin/php-saml/src/Saml2/schemas/xenc-schema.xsd new file mode 100644 index 0000000000..d6d79103ce --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/schemas/xenc-schema.xsd @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/onelogin/php-saml/src/Saml2/schemas/xml.xsd b/vendor/onelogin/php-saml/src/Saml2/schemas/xml.xsd new file mode 100644 index 0000000000..aea7d0db0a --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/schemas/xml.xsd @@ -0,0 +1,287 @@ + + + + + + +
+

About the XML namespace

+ +
+

+ This schema document describes the XML namespace, in a form + suitable for import by other schema documents. +

+

+ See + http://www.w3.org/XML/1998/namespace.html and + + http://www.w3.org/TR/REC-xml for information + about this namespace. +

+

+ Note that local names in this namespace are intended to be + defined only by the World Wide Web Consortium or its subgroups. + The names currently defined in this namespace are listed below. + They should not be used with conflicting semantics by any Working + Group, specification, or document instance. +

+

+ See further below in this document for more information about how to refer to this schema document from your own + XSD schema documents and about the + namespace-versioning policy governing this schema document. +

+
+
+
+
+ + + + +
+ +

lang (as an attribute name)

+

+ denotes an attribute whose value + is a language code for the natural language of the content of + any element; its value is inherited. This name is reserved + by virtue of its definition in the XML specification.

+ +
+
+

Notes

+

+ Attempting to install the relevant ISO 2- and 3-letter + codes as the enumerated possible values is probably never + going to be a realistic possibility. +

+

+ See BCP 47 at + http://www.rfc-editor.org/rfc/bcp/bcp47.txt + and the IANA language subtag registry at + + http://www.iana.org/assignments/language-subtag-registry + for further information. +

+

+ The union allows for the 'un-declaration' of xml:lang with + the empty string. +

+
+
+
+ + + + + + + + + +
+ + + + +
+ +

space (as an attribute name)

+

+ denotes an attribute whose + value is a keyword indicating what whitespace processing + discipline is intended for the content of the element; its + value is inherited. This name is reserved by virtue of its + definition in the XML specification.

+ +
+
+
+ + + + + + +
+ + + +
+ +

base (as an attribute name)

+

+ denotes an attribute whose value + provides a URI to be used as the base for interpreting any + relative URIs in the scope of the element on which it + appears; its value is inherited. This name is reserved + by virtue of its definition in the XML Base specification.

+ +

+ See http://www.w3.org/TR/xmlbase/ + for information about this attribute. +

+
+
+
+
+ + + + +
+ +

id (as an attribute name)

+

+ denotes an attribute whose value + should be interpreted as if declared to be of type ID. + This name is reserved by virtue of its definition in the + xml:id specification.

+ +

+ See http://www.w3.org/TR/xml-id/ + for information about this attribute. +

+
+
+
+
+ + + + + + + + + + +
+ +

Father (in any context at all)

+ +
+

+ denotes Jon Bosak, the chair of + the original XML Working Group. This name is reserved by + the following decision of the W3C XML Plenary and + XML Coordination groups: +

+
+

+ In appreciation for his vision, leadership and + dedication the W3C XML Plenary on this 10th day of + February, 2000, reserves for Jon Bosak in perpetuity + the XML name "xml:Father". +

+
+
+
+
+
+ + + +
+

About this schema document

+ +
+

+ This schema defines attributes and an attribute group suitable + for use by schemas wishing to allow xml:base, + xml:lang, xml:space or + xml:id attributes on elements they define. +

+

+ To enable this, such a schema must import this schema for + the XML namespace, e.g. as follows: +

+
+          <schema . . .>
+           . . .
+           <import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+     
+

+ or +

+
+           <import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+     
+

+ Subsequently, qualified reference to any of the attributes or the + group defined below will have the desired effect, e.g. +

+
+          <type . . .>
+           . . .
+           <attributeGroup ref="xml:specialAttrs"/>
+     
+

+ will define a type which will schema-validate an instance element + with any of those attributes. +

+
+
+
+
+ + + +
+

Versioning policy for this schema document

+
+

+ In keeping with the XML Schema WG's standard versioning + policy, this schema document will persist at + + http://www.w3.org/2009/01/xml.xsd. +

+

+ At the date of issue it can also be found at + + http://www.w3.org/2001/xml.xsd. +

+

+ The schema document at that URI may however change in the future, + in order to remain compatible with the latest version of XML + Schema itself, or with the XML namespace itself. In other words, + if the XML Schema or XML namespaces change, the version of this + document at + http://www.w3.org/2001/xml.xsd + + will change accordingly; the version at + + http://www.w3.org/2009/01/xml.xsd + + will not change. +

+

+ Previous dated (and unchanging) versions of this schema + document are at: +

+ +
+
+
+
+ +
+ diff --git a/vendor/onelogin/php-saml/src/Saml2/schemas/xmldsig-core-schema.xsd b/vendor/onelogin/php-saml/src/Saml2/schemas/xmldsig-core-schema.xsd new file mode 100644 index 0000000000..6f5acc75af --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/schemas/xmldsig-core-schema.xsd @@ -0,0 +1,309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/onelogin/php-saml/src/Saml2/version.json b/vendor/onelogin/php-saml/src/Saml2/version.json new file mode 100644 index 0000000000..d0a2f77cc6 --- /dev/null +++ b/vendor/onelogin/php-saml/src/Saml2/version.json @@ -0,0 +1,7 @@ +{ + "php-saml": { + "version": "3.3.1", + "released": "06/11/2019" + } +} + diff --git a/vendor/phpmailer/phpmailer/COMMITMENT b/vendor/phpmailer/phpmailer/COMMITMENT new file mode 100644 index 0000000000..a687e0ddb6 --- /dev/null +++ b/vendor/phpmailer/phpmailer/COMMITMENT @@ -0,0 +1,46 @@ +GPL Cooperation Commitment +Version 1.0 + +Before filing or continuing to prosecute any legal proceeding or claim +(other than a Defensive Action) arising from termination of a Covered +License, we commit to extend to the person or entity ('you') accused +of violating the Covered License the following provisions regarding +cure and reinstatement, taken from GPL version 3. As used here, the +term 'this License' refers to the specific Covered License being +enforced. + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly + and finally terminates your license, and (b) permanently, if the + copyright holder fails to notify you of the violation by some + reasonable means prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you + have received notice of violation of this License (for any work) + from that copyright holder, and you cure the violation prior to 30 + days after your receipt of the notice. + +We intend this Commitment to be irrevocable, and binding and +enforceable against us and assignees of or successors to our +copyrights. + +Definitions + +'Covered License' means the GNU General Public License, version 2 +(GPLv2), the GNU Lesser General Public License, version 2.1 +(LGPLv2.1), or the GNU Library General Public License, version 2 +(LGPLv2), all as published by the Free Software Foundation. + +'Defensive Action' means a legal proceeding or claim that We bring +against you in response to a prior proceeding or claim initiated by +you or your affiliate. + +'We' means each contributor to this repository as of the date of +inclusion of this file, including subsidiaries of a corporate +contributor. + +This work is available under a Creative Commons Attribution-ShareAlike +4.0 International license (https://creativecommons.org/licenses/by-sa/4.0/). diff --git a/vendor/phpmailer/phpmailer/LICENSE b/vendor/phpmailer/phpmailer/LICENSE new file mode 100644 index 0000000000..f166cc57b2 --- /dev/null +++ b/vendor/phpmailer/phpmailer/LICENSE @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/vendor/phpmailer/phpmailer/README.md b/vendor/phpmailer/phpmailer/README.md new file mode 100644 index 0000000000..a000c93035 --- /dev/null +++ b/vendor/phpmailer/phpmailer/README.md @@ -0,0 +1,221 @@ +![PHPMailer](https://raw.github.com/PHPMailer/PHPMailer/master/examples/images/phpmailer.png) + +# PHPMailer - A full-featured email creation and transfer class for PHP + +Build status: [![Build Status](https://travis-ci.org/PHPMailer/PHPMailer.svg)](https://travis-ci.org/PHPMailer/PHPMailer) +[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/PHPMailer/PHPMailer/badges/quality-score.png?s=3758e21d279becdf847a557a56a3ed16dfec9d5d)](https://scrutinizer-ci.com/g/PHPMailer/PHPMailer/) +[![Code Coverage](https://scrutinizer-ci.com/g/PHPMailer/PHPMailer/badges/coverage.png?s=3fe6ca5fe8cd2cdf96285756e42932f7ca256962)](https://scrutinizer-ci.com/g/PHPMailer/PHPMailer/) + +[![Latest Stable Version](https://poser.pugx.org/phpmailer/phpmailer/v/stable.svg)](https://packagist.org/packages/phpmailer/phpmailer) [![Total Downloads](https://poser.pugx.org/phpmailer/phpmailer/downloads)](https://packagist.org/packages/phpmailer/phpmailer) [![Latest Unstable Version](https://poser.pugx.org/phpmailer/phpmailer/v/unstable.svg)](https://packagist.org/packages/phpmailer/phpmailer) [![License](https://poser.pugx.org/phpmailer/phpmailer/license.svg)](https://packagist.org/packages/phpmailer/phpmailer) + +## Class Features +- Probably the world's most popular code for sending email from PHP! +- Used by many open-source projects: WordPress, Drupal, 1CRM, SugarCRM, Yii, Joomla!, and many more +- Integrated SMTP support - send without a local mail server +- Send emails with multiple To, CC, BCC and Reply-to addresses +- Multipart/alternative emails for mail clients that do not read HTML email +- Add attachments, including inline +- Support for UTF-8 content and 8bit, base64, binary, and quoted-printable encodings +- SMTP authentication with LOGIN, PLAIN, CRAM-MD5, and XOAUTH2 mechanisms over SSL and SMTP+STARTTLS transports +- Validates email addresses automatically +- Protect against header injection attacks +- Error messages in over 50 languages! +- DKIM and S/MIME signing support +- Compatible with PHP 5.5 and later +- Namespaced to prevent name clashes +- Much more! + +## Why you might need it +Many PHP developers need to send email from their code. The only PHP function that supports this is [`mail()`](https://www.php.net/manual/en/function.mail.php). However, it does not provide any assistance for making use of popular features such as encryption, authentication, HTML messages, and attachments. + +Formatting email correctly is surprisingly difficult. There are myriad overlapping RFCs, requiring tight adherence to horribly complicated formatting and encoding rules – the vast majority of code that you'll find online that uses the `mail()` function directly is just plain wrong! +*Please* don't be tempted to do it yourself – if you don't use PHPMailer, there are many other excellent libraries that you should look at before rolling your own. Try [SwiftMailer](https://swiftmailer.symfony.com/), [Zend/Mail](https://zendframework.github.io/zend-mail/), [ZetaComponents](https://github.com/zetacomponents/Mail) etc. + +The PHP `mail()` function usually sends via a local mail server, typically fronted by a `sendmail` binary on Linux, BSD, and macOS platforms, however, Windows usually doesn't include a local mail server; PHPMailer's integrated SMTP implementation allows email sending on Windows platforms without a local mail server. + +## License +This software is distributed under the [LGPL 2.1](http://www.gnu.org/licenses/lgpl-2.1.html) license, along with the [GPL Cooperation Commitment](https://gplcc.github.io/gplcc/). Please read LICENSE for information on the software availability and distribution. + +## Installation & loading +PHPMailer is available on [Packagist](https://packagist.org/packages/phpmailer/phpmailer) (using semantic versioning), and installation via [Composer](https://getcomposer.org) is the recommended way to install PHPMailer. Just add this line to your `composer.json` file: + +```json +"phpmailer/phpmailer": "~6.1" +``` + +or run + +```sh +composer require phpmailer/phpmailer +``` + +Note that the `vendor` folder and the `vendor/autoload.php` script are generated by Composer; they are not part of PHPMailer. + +If you want to use the Gmail XOAUTH2 authentication class, you will also need to add a dependency on the `league/oauth2-client` package in your `composer.json`. + +Alternatively, if you're not using Composer, copy the contents of the PHPMailer folder into one of the `include_path` directories specified in your PHP configuration and load each class file manually: + +```php +SMTPDebug = SMTP::DEBUG_SERVER; // Enable verbose debug output + $mail->isSMTP(); // Send using SMTP + $mail->Host = 'smtp1.example.com'; // Set the SMTP server to send through + $mail->SMTPAuth = true; // Enable SMTP authentication + $mail->Username = 'user@example.com'; // SMTP username + $mail->Password = 'secret'; // SMTP password + $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // Enable TLS encryption; `PHPMailer::ENCRYPTION_SMTPS` also accepted + $mail->Port = 587; // TCP port to connect to + + //Recipients + $mail->setFrom('from@example.com', 'Mailer'); + $mail->addAddress('joe@example.net', 'Joe User'); // Add a recipient + $mail->addAddress('ellen@example.com'); // Name is optional + $mail->addReplyTo('info@example.com', 'Information'); + $mail->addCC('cc@example.com'); + $mail->addBCC('bcc@example.com'); + + // Attachments + $mail->addAttachment('/var/tmp/file.tar.gz'); // Add attachments + $mail->addAttachment('/tmp/image.jpg', 'new.jpg'); // Optional name + + // Content + $mail->isHTML(true); // Set email format to HTML + $mail->Subject = 'Here is the subject'; + $mail->Body = 'This is the HTML message body in bold!'; + $mail->AltBody = 'This is the body in plain text for non-HTML mail clients'; + + $mail->send(); + echo 'Message has been sent'; +} catch (Exception $e) { + echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}"; +} +``` + +You'll find plenty more to play with in the [examples](https://github.com/PHPMailer/PHPMailer/tree/master/examples) folder. + +If you are re-using the instance (e.g. when sending to a mailing list), you may need to clear the recipient list to avoid sending duplicate messages. See [the mailing list example](https://github.com/PHPMailer/PHPMailer/blob/master/examples/mailing_list.phps) for further guidance. + +That's it. You should now be ready to use PHPMailer! + +## Localization +PHPMailer defaults to English, but in the [language](https://github.com/PHPMailer/PHPMailer/tree/master/language/) folder you'll find many translations for PHPMailer error messages that you may encounter. Their filenames contain [ISO 639-1](http://en.wikipedia.org/wiki/ISO_639-1) language code for the translations, for example `fr` for French. To specify a language, you need to tell PHPMailer which one to use, like this: + +```php +// To load the French version +$mail->setLanguage('fr', '/optional/path/to/language/directory/'); +``` + +We welcome corrections and new languages - if you're looking for corrections to do, run the [PHPMailerLangTest.php](https://github.com/PHPMailer/PHPMailer/tree/master/test/PHPMailerLangTest.php) script in the tests folder and it will show any missing translations. + +## Documentation +Start reading at the [GitHub wiki](https://github.com/PHPMailer/PHPMailer/wiki). If you're having trouble, this should be the first place you look as it's the most frequently updated. + +Examples of how to use PHPMailer for common scenarios can be found in the [examples](https://github.com/PHPMailer/PHPMailer/tree/master/examples) folder. If you're looking for a good starting point, we recommend you start with [the Gmail example](https://github.com/PHPMailer/PHPMailer/tree/master/examples/gmail.phps). + +Note that in order to reduce PHPMailer's deployed code footprint, the examples are no longer included if you load PHPMailer via Composer or via [GitHub's zip file download](https://github.com/PHPMailer/PHPMailer/archive/master.zip), so you'll need to either clone the git repository or use the above links to get to the examples directly. + +Complete generated API documentation is [available online](http://phpmailer.github.io/PHPMailer/). + +You can generate complete API-level documentation by running `phpdoc` in the top-level folder, and documentation will appear in the `docs` folder, though you'll need to have [PHPDocumentor](http://www.phpdoc.org) installed. You may find [the unit tests](https://github.com/PHPMailer/PHPMailer/tree/master/test/phpmailerTest.php) a good source of how to do various operations such as encryption. + +If the documentation doesn't cover what you need, search the [many questions on Stack Overflow](http://stackoverflow.com/questions/tagged/phpmailer), and before you ask a question about "SMTP Error: Could not connect to SMTP host.", [read the troubleshooting guide](https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting). + +## Tests +There is a PHPUnit test script in the [test](https://github.com/PHPMailer/PHPMailer/tree/master/test/) folder. PHPMailer uses PHPUnit 4.8 - we would use 5.x but we need to run on PHP 5.5. + +Build status: [![Build Status](https://travis-ci.org/PHPMailer/PHPMailer.svg)](https://travis-ci.org/PHPMailer/PHPMailer) + +If this isn't passing, is there something you can do to help? + +## Security +Please disclose any vulnerabilities found responsibly - report any security problems found to the maintainers privately. + +PHPMailer versions prior to 5.2.22 (released January 9th 2017) have a local file disclosure vulnerability, [CVE-2017-5223](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2017-5223). If content passed into `msgHTML()` is sourced from unfiltered user input, relative paths can map to absolute local file paths and added as attachments. Also note that `addAttachment` (just like `file_get_contents`, `passthru`, `unlink`, etc) should not be passed user-sourced params either! Reported by Yongxiang Li of Asiasecurity. + +PHPMailer versions prior to 5.2.20 (released December 28th 2016) are vulnerable to [CVE-2016-10045](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-10045) a remote code execution vulnerability, responsibly reported by [Dawid Golunski](https://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10045-Vuln-Patch-Bypass.html), and patched by Paul Buonopane (@Zenexer). + +PHPMailer versions prior to 5.2.18 (released December 2016) are vulnerable to [CVE-2016-10033](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-10033) a critical remote code execution vulnerability, responsibly reported by [Dawid Golunski](http://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10033-Vuln.html). + +See [SECURITY](https://github.com/PHPMailer/PHPMailer/tree/master/SECURITY.md) for more detail on security issues. + +## Contributing +Please submit bug reports, suggestions and pull requests to the [GitHub issue tracker](https://github.com/PHPMailer/PHPMailer/issues). + +We're particularly interested in fixing edge-cases, expanding test coverage and updating translations. + +If you found a mistake in the docs, or want to add something, go ahead and amend the wiki - anyone can edit it. + +If you have git clones from prior to the move to the PHPMailer GitHub organisation, you'll need to update any remote URLs referencing the old GitHub location with a command like this from within your clone: + +```sh +git remote set-url upstream https://github.com/PHPMailer/PHPMailer.git +``` + +Please *don't* use the SourceForge or Google Code projects any more; they are obsolete and no longer maintained. + +## Sponsorship +Development time and resources for PHPMailer are provided by [Smartmessages.net](https://info.smartmessages.net/), a powerful email marketing system. + +Smartmessages email marketing + +Other contributions are gladly received, whether in beer 🍺, T-shirts 👕, Amazon wishlist raids, or cold, hard cash 💰. If you'd like to donate to say "thank you" to maintainers or contributors, please contact them through individual profile pages via [the contributors page](https://github.com/PHPMailer/PHPMailer/graphs/contributors). + +## Changelog +See [changelog](changelog.md). + +## History +- PHPMailer was originally written in 2001 by Brent R. Matzelle as a [SourceForge project](http://sourceforge.net/projects/phpmailer/). +- Marcus Bointon (coolbru on SF) and Andy Prevost (codeworxtech) took over the project in 2004. +- Became an Apache incubator project on Google Code in 2010, managed by Jim Jagielski. +- Marcus created his fork on [GitHub](https://github.com/Synchro/PHPMailer) in 2008. +- Jim and Marcus decide to join forces and use GitHub as the canonical and official repo for PHPMailer in 2013. +- PHPMailer moves to the [PHPMailer organisation](https://github.com/PHPMailer) on GitHub in 2013. + +### What's changed since moving from SourceForge? +- Official successor to the SourceForge and Google Code projects. +- Test suite. +- Continuous integration with Travis-CI. +- Composer support. +- Public development. +- Additional languages and language strings. +- CRAM-MD5 authentication support. +- Preserves full repo history of authors, commits and branches from the original SourceForge project. diff --git a/vendor/phpmailer/phpmailer/SECURITY.md b/vendor/phpmailer/phpmailer/SECURITY.md new file mode 100644 index 0000000000..5e917cd04a --- /dev/null +++ b/vendor/phpmailer/phpmailer/SECURITY.md @@ -0,0 +1,28 @@ +# Security notices relating to PHPMailer + +Please disclose any vulnerabilities found responsibly - report any security problems found to the maintainers privately. + +PHPMailer versions prior to 6.0.6 and 5.2.27 are vulnerable to an object injection attack by passing `phar://` paths into `addAttachment()` and other functions that may receive unfiltered local paths, possibly leading to RCE. Recorded as [CVE-2018-19296](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2018-19296). See [this article](https://knasmueller.net/5-answers-about-php-phar-exploitation) for more info on this type of vulnerability. Mitigated by blocking the use of paths containing URL-protocol style prefixes such as `phar://`. Reported by Sehun Oh of cyberone.kr. + +PHPMailer versions prior to 5.2.24 (released July 26th 2017) have an XSS vulnerability in one of the code examples, [CVE-2017-11503](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2017-11503). The `code_generator.phps` example did not filter user input prior to output. This file is distributed with a `.phps` extension, so it it not normally executable unless it is explicitly renamed, and the file is not included when PHPMailer is loaded through composer, so it is safe by default. There was also an undisclosed potential XSS vulnerability in the default exception handler (unused by default). Patches for both issues kindly provided by Patrick Monnerat of the Fedora Project. + +PHPMailer versions prior to 5.2.22 (released January 9th 2017) have a local file disclosure vulnerability, [CVE-2017-5223](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2017-5223). If content passed into `msgHTML()` is sourced from unfiltered user input, relative paths can map to absolute local file paths and added as attachments. Also note that `addAttachment` (just like `file_get_contents`, `passthru`, `unlink`, etc) should not be passed user-sourced params either! Reported by Yongxiang Li of Asiasecurity. + +PHPMailer versions prior to 5.2.20 (released December 28th 2016) are vulnerable to [CVE-2016-10045](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-10045) a remote code execution vulnerability, responsibly reported by [Dawid Golunski](https://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10045-Vuln-Patch-Bypass.html), and patched by Paul Buonopane (@Zenexer). + +PHPMailer versions prior to 5.2.18 (released December 2016) are vulnerable to [CVE-2016-10033](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-10033) a remote code execution vulnerability, responsibly reported by [Dawid Golunski](http://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10033-Vuln.html). + +PHPMailer versions prior to 5.2.14 (released November 2015) are vulnerable to [CVE-2015-8476](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2015-8476) an SMTP CRLF injection bug permitting arbitrary message sending. + +PHPMailer versions prior to 5.2.10 (released May 2015) are vulnerable to [CVE-2008-5619](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2008-5619), a remote code execution vulnerability in the bundled html2text library. This file was removed in 5.2.10, so if you are using a version prior to that and make use of the html2text function, it's vitally important that you upgrade and remove this file. + +PHPMailer versions prior to 2.0.7 and 2.2.1 are vulnerable to [CVE-2012-0796](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2012-0796), an email header injection attack. + +Joomla 1.6.0 uses PHPMailer in an unsafe way, allowing it to reveal local file paths, reported in [CVE-2011-3747](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2011-3747). + +PHPMailer didn't sanitise the `$lang_path` parameter in `SetLanguage`. This wasn't a problem in itself, but some apps (PHPClassifieds, ATutor) also failed to sanitise user-provided parameters passed to it, permitting semi-arbitrary local file inclusion, reported in [CVE-2010-4914](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2010-4914), [CVE-2007-2021](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2007-2021) and [CVE-2006-5734](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2006-5734). + +PHPMailer 1.7.2 and earlier contained a possible DDoS vulnerability reported in [CVE-2005-1807](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2005-1807). + +PHPMailer 1.7 and earlier (June 2003) have a possible vulnerability in the `SendmailSend` method where shell commands may not be sanitised. Reported in [CVE-2007-3215](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2007-3215). + diff --git a/vendor/phpmailer/phpmailer/VERSION b/vendor/phpmailer/phpmailer/VERSION new file mode 100644 index 0000000000..132c6def58 --- /dev/null +++ b/vendor/phpmailer/phpmailer/VERSION @@ -0,0 +1 @@ +6.1.1 \ No newline at end of file diff --git a/vendor/phpmailer/phpmailer/composer.json b/vendor/phpmailer/phpmailer/composer.json new file mode 100644 index 0000000000..d1b14171b6 --- /dev/null +++ b/vendor/phpmailer/phpmailer/composer.json @@ -0,0 +1,55 @@ +{ + "name": "phpmailer/phpmailer", + "type": "library", + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "require": { + "php": ">=5.5.0", + "ext-ctype": "*", + "ext-filter": "*" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.2", + "phpdocumentor/phpdocumentor": "2.*", + "phpunit/phpunit": "^4.8 || ^5.7", + "zendframework/zend-serializer": "2.7.*", + "doctrine/annotations": "1.2.*", + "zendframework/zend-eventmanager": "3.0.*", + "zendframework/zend-i18n": "2.7.3" + }, + "suggest": { + "psr/log": "For optional PSR-3 debug logging", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication", + "ext-mbstring": "Needed to send email in multibyte encoding charset", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)" + }, + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "PHPMailer\\Test\\": "test/" + } + }, + "license": "LGPL-2.1-only" +} diff --git a/vendor/phpmailer/phpmailer/get_oauth_token.php b/vendor/phpmailer/phpmailer/get_oauth_token.php new file mode 100644 index 0000000000..1237b57be8 --- /dev/null +++ b/vendor/phpmailer/phpmailer/get_oauth_token.php @@ -0,0 +1,144 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2017 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ +/** + * Get an OAuth2 token from an OAuth2 provider. + * * Install this script on your server so that it's accessible + * as [https/http]:////get_oauth_token.php + * e.g.: http://localhost/phpmailer/get_oauth_token.php + * * Ensure dependencies are installed with 'composer install' + * * Set up an app in your Google/Yahoo/Microsoft account + * * Set the script address as the app's redirect URL + * If no refresh token is obtained when running this file, + * revoke access to your app and run the script again. + */ + +namespace PHPMailer\PHPMailer; + +/** + * Aliases for League Provider Classes + * Make sure you have added these to your composer.json and run `composer install` + * Plenty to choose from here: + * @see http://oauth2-client.thephpleague.com/providers/thirdparty/ + */ +// @see https://github.com/thephpleague/oauth2-google +use League\OAuth2\Client\Provider\Google; +// @see https://packagist.org/packages/hayageek/oauth2-yahoo +use Hayageek\OAuth2\Client\Provider\Yahoo; +// @see https://github.com/stevenmaguire/oauth2-microsoft +use Stevenmaguire\OAuth2\Client\Provider\Microsoft; + +if (!isset($_GET['code']) && !isset($_GET['provider'])) { +?> + +Select Provider:
+Google
+Yahoo
+Microsoft/Outlook/Hotmail/Live/Office365
+ + + $clientId, + 'clientSecret' => $clientSecret, + 'redirectUri' => $redirectUri, + 'accessType' => 'offline' +]; + +$options = []; +$provider = null; + +switch ($providerName) { + case 'Google': + $provider = new Google($params); + $options = [ + 'scope' => [ + 'https://mail.google.com/' + ] + ]; + break; + case 'Yahoo': + $provider = new Yahoo($params); + break; + case 'Microsoft': + $provider = new Microsoft($params); + $options = [ + 'scope' => [ + 'wl.imap', + 'wl.offline_access' + ] + ]; + break; +} + +if (null === $provider) { + exit('Provider missing'); +} + +if (!isset($_GET['code'])) { + // If we don't have an authorization code then get one + $authUrl = $provider->getAuthorizationUrl($options); + $_SESSION['oauth2state'] = $provider->getState(); + header('Location: ' . $authUrl); + exit; +// Check given state against previously stored one to mitigate CSRF attack +} elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) { + unset($_SESSION['oauth2state']); + unset($_SESSION['provider']); + exit('Invalid state'); +} else { + unset($_SESSION['provider']); + // Try to get an access token (using the authorization code grant) + $token = $provider->getAccessToken( + 'authorization_code', + [ + 'code' => $_GET['code'] + ] + ); + // Use this to interact with an API on the users behalf + // Use this to get a new access token if the old one expires + echo 'Refresh Token: ', $token->getRefreshToken(); +} diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-af.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-af.php new file mode 100644 index 0000000000..3c42d78e15 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-af.php @@ -0,0 +1,25 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP -ի սխալ: չհաջողվեց ստուգել իսկությունը.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP -ի սխալ: չհաջողվեց կապ հաստատել SMTP սերվերի հետ.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP -ի սխալ: տվյալները ընդունված չեն.'; +$PHPMAILER_LANG['empty_message'] = 'Հաղորդագրությունը դատարկ է'; +$PHPMAILER_LANG['encoding'] = 'Կոդավորման անհայտ տեսակ: '; +$PHPMAILER_LANG['execute'] = 'Չհաջողվեց իրականացնել հրամանը: '; +$PHPMAILER_LANG['file_access'] = 'Ֆայլը հասանելի չէ: '; +$PHPMAILER_LANG['file_open'] = 'Ֆայլի սխալ: ֆայլը չհաջողվեց բացել: '; +$PHPMAILER_LANG['from_failed'] = 'Ուղարկողի հետևյալ հասցեն սխալ է: '; +$PHPMAILER_LANG['instantiate'] = 'Հնարավոր չէ կանչել mail ֆունկցիան.'; +$PHPMAILER_LANG['invalid_address'] = 'Հասցեն սխալ է: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' փոստային սերվերի հետ չի աշխատում.'; +$PHPMAILER_LANG['provide_address'] = 'Անհրաժեշտ է տրամադրել գոնե մեկ ստացողի e-mail հասցե.'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP -ի սխալ: չի հաջողվել ուղարկել հետևյալ ստացողների հասցեներին: '; +$PHPMAILER_LANG['signing'] = 'Ստորագրման սխալ: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP -ի connect() ֆունկցիան չի հաջողվել'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP սերվերի սխալ: '; +$PHPMAILER_LANG['variable_set'] = 'Չի հաջողվում ստեղծել կամ վերափոխել փոփոխականը: '; +$PHPMAILER_LANG['extension_missing'] = 'Հավելվածը բացակայում է: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-ar.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ar.php new file mode 100644 index 0000000000..865d0b7329 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ar.php @@ -0,0 +1,27 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'خطأ SMTP : لا يمكن تأكيد الهوية.'; +$PHPMAILER_LANG['connect_host'] = 'خطأ SMTP: لا يمكن الاتصال بالخادم SMTP.'; +$PHPMAILER_LANG['data_not_accepted'] = 'خطأ SMTP: لم يتم قبول المعلومات .'; +$PHPMAILER_LANG['empty_message'] = 'نص الرسالة فارغ'; +$PHPMAILER_LANG['encoding'] = 'ترميز غير معروف: '; +$PHPMAILER_LANG['execute'] = 'لا يمكن تنفيذ : '; +$PHPMAILER_LANG['file_access'] = 'لا يمكن الوصول للملف: '; +$PHPMAILER_LANG['file_open'] = 'خطأ في الملف: لا يمكن فتحه: '; +$PHPMAILER_LANG['from_failed'] = 'خطأ على مستوى عنوان المرسل : '; +$PHPMAILER_LANG['instantiate'] = 'لا يمكن توفير خدمة البريد.'; +$PHPMAILER_LANG['invalid_address'] = 'الإرسال غير ممكن لأن عنوان البريد الإلكتروني غير صالح: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' برنامج الإرسال غير مدعوم.'; +$PHPMAILER_LANG['provide_address'] = 'يجب توفير عنوان البريد الإلكتروني لمستلم واحد على الأقل.'; +$PHPMAILER_LANG['recipients_failed'] = 'خطأ SMTP: الأخطاء التالية ' . + 'فشل في الارسال لكل من : '; +$PHPMAILER_LANG['signing'] = 'خطأ في التوقيع: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() غير ممكن.'; +$PHPMAILER_LANG['smtp_error'] = 'خطأ على مستوى الخادم SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'لا يمكن تعيين أو إعادة تعيين متغير: '; +$PHPMAILER_LANG['extension_missing'] = 'الإضافة غير موجودة: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-az.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-az.php new file mode 100644 index 0000000000..3749d83d60 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-az.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP Greška: Neuspjela prijava.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP Greška: Nije moguće spojiti se sa SMTP serverom.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP Greška: Podatci nisu prihvaćeni.'; +$PHPMAILER_LANG['empty_message'] = 'Sadržaj poruke je prazan.'; +$PHPMAILER_LANG['encoding'] = 'Nepoznata kriptografija: '; +$PHPMAILER_LANG['execute'] = 'Nije moguće izvršiti naredbu: '; +$PHPMAILER_LANG['file_access'] = 'Nije moguće pristupiti datoteci: '; +$PHPMAILER_LANG['file_open'] = 'Nije moguće otvoriti datoteku: '; +$PHPMAILER_LANG['from_failed'] = 'SMTP Greška: Slanje sa navedenih e-mail adresa nije uspjelo: '; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP Greška: Slanje na navedene e-mail adrese nije uspjelo: '; +$PHPMAILER_LANG['instantiate'] = 'Ne mogu pokrenuti mail funkcionalnost.'; +$PHPMAILER_LANG['invalid_address'] = 'E-mail nije poslan. Neispravna e-mail adresa: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer nije podržan.'; +$PHPMAILER_LANG['provide_address'] = 'Definišite barem jednu adresu primaoca.'; +$PHPMAILER_LANG['signing'] = 'Greška prilikom prijave: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Spajanje na SMTP server nije uspjelo.'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP greška: '; +$PHPMAILER_LANG['variable_set'] = 'Nije moguće postaviti varijablu ili je vratiti nazad: '; +$PHPMAILER_LANG['extension_missing'] = 'Nedostaje ekstenzija: '; \ No newline at end of file diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-be.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-be.php new file mode 100644 index 0000000000..e2f98f0f6d --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-be.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'Памылка SMTP: памылка ідэнтыфікацыі.'; +$PHPMAILER_LANG['connect_host'] = 'Памылка SMTP: нельга ўстанавіць сувязь з SMTP-серверам.'; +$PHPMAILER_LANG['data_not_accepted'] = 'Памылка SMTP: звесткі непрынятыя.'; +$PHPMAILER_LANG['empty_message'] = 'Пустое паведамленне.'; +$PHPMAILER_LANG['encoding'] = 'Невядомая кадыроўка тэксту: '; +$PHPMAILER_LANG['execute'] = 'Нельга выканаць каманду: '; +$PHPMAILER_LANG['file_access'] = 'Няма доступу да файла: '; +$PHPMAILER_LANG['file_open'] = 'Нельга адкрыць файл: '; +$PHPMAILER_LANG['from_failed'] = 'Няправільны адрас адпраўніка: '; +$PHPMAILER_LANG['instantiate'] = 'Нельга прымяніць функцыю mail().'; +$PHPMAILER_LANG['invalid_address'] = 'Нельга даслаць паведамленне, няправільны email атрымальніка: '; +$PHPMAILER_LANG['provide_address'] = 'Запоўніце, калі ласка, правільны email атрымальніка.'; +$PHPMAILER_LANG['mailer_not_supported'] = ' - паштовы сервер не падтрымліваецца.'; +$PHPMAILER_LANG['recipients_failed'] = 'Памылка SMTP: няправільныя атрымальнікі: '; +$PHPMAILER_LANG['signing'] = 'Памылка подпісу паведамлення: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Памылка сувязі з SMTP-серверам.'; +$PHPMAILER_LANG['smtp_error'] = 'Памылка SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'Нельга ўстанавіць або перамяніць значэнне пераменнай: '; +//$PHPMAILER_LANG['extension_missing'] = 'Extension missing: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-bg.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-bg.php new file mode 100644 index 0000000000..b22941f6b5 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-bg.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP грешка: Не може да се удостовери пред сървъра.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP грешка: Не може да се свърже с SMTP хоста.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP грешка: данните не са приети.'; +$PHPMAILER_LANG['empty_message'] = 'Съдържанието на съобщението е празно'; +$PHPMAILER_LANG['encoding'] = 'Неизвестно кодиране: '; +$PHPMAILER_LANG['execute'] = 'Не може да се изпълни: '; +$PHPMAILER_LANG['file_access'] = 'Няма достъп до файл: '; +$PHPMAILER_LANG['file_open'] = 'Файлова грешка: Не може да се отвори файл: '; +$PHPMAILER_LANG['from_failed'] = 'Следните адреси за подател са невалидни: '; +$PHPMAILER_LANG['instantiate'] = 'Не може да се инстанцира функцията mail.'; +$PHPMAILER_LANG['invalid_address'] = 'Невалиден адрес: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' - пощенски сървър не се поддържа.'; +$PHPMAILER_LANG['provide_address'] = 'Трябва да предоставите поне един email адрес за получател.'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP грешка: Следните адреси за Получател са невалидни: '; +$PHPMAILER_LANG['signing'] = 'Грешка при подписване: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP провален connect().'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP сървърна грешка: '; +$PHPMAILER_LANG['variable_set'] = 'Не може да се установи или възстанови променлива: '; +$PHPMAILER_LANG['extension_missing'] = 'Липсва разширение: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-ca.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ca.php new file mode 100644 index 0000000000..4117596c6f --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ca.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'Error SMTP: No s’ha pogut autenticar.'; +$PHPMAILER_LANG['connect_host'] = 'Error SMTP: No es pot connectar al servidor SMTP.'; +$PHPMAILER_LANG['data_not_accepted'] = 'Error SMTP: Dades no acceptades.'; +$PHPMAILER_LANG['empty_message'] = 'El cos del missatge està buit.'; +$PHPMAILER_LANG['encoding'] = 'Codificació desconeguda: '; +$PHPMAILER_LANG['execute'] = 'No es pot executar: '; +$PHPMAILER_LANG['file_access'] = 'No es pot accedir a l’arxiu: '; +$PHPMAILER_LANG['file_open'] = 'Error d’Arxiu: No es pot obrir l’arxiu: '; +$PHPMAILER_LANG['from_failed'] = 'La(s) següent(s) adreces de remitent han fallat: '; +$PHPMAILER_LANG['instantiate'] = 'No s’ha pogut crear una instància de la funció Mail.'; +$PHPMAILER_LANG['invalid_address'] = 'Adreça d’email invalida: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer no està suportat'; +$PHPMAILER_LANG['provide_address'] = 'S’ha de proveir almenys una adreça d’email com a destinatari.'; +$PHPMAILER_LANG['recipients_failed'] = 'Error SMTP: Els següents destinataris han fallat: '; +$PHPMAILER_LANG['signing'] = 'Error al signar: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Ha fallat el SMTP Connect().'; +$PHPMAILER_LANG['smtp_error'] = 'Error del servidor SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'No s’ha pogut establir o restablir la variable: '; +//$PHPMAILER_LANG['extension_missing'] = 'Extension missing: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-ch.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ch.php new file mode 100644 index 0000000000..4fda6b85d7 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ch.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP 错误:身份验证失败。'; +$PHPMAILER_LANG['connect_host'] = 'SMTP 错误: 不能连接SMTP主机。'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP 错误: 数据不可接受。'; +//$PHPMAILER_LANG['empty_message'] = 'Message body empty'; +$PHPMAILER_LANG['encoding'] = '未知编码:'; +$PHPMAILER_LANG['execute'] = '不能执行: '; +$PHPMAILER_LANG['file_access'] = '不能访问文件:'; +$PHPMAILER_LANG['file_open'] = '文件错误:不能打开文件:'; +$PHPMAILER_LANG['from_failed'] = '下面的发送地址邮件发送失败了: '; +$PHPMAILER_LANG['instantiate'] = '不能实现mail方法。'; +//$PHPMAILER_LANG['invalid_address'] = 'Invalid address: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' 您所选择的发送邮件的方法并不支持。'; +$PHPMAILER_LANG['provide_address'] = '您必须提供至少一个 收信人的email地址。'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP 错误: 下面的 收件人失败了: '; +//$PHPMAILER_LANG['signing'] = 'Signing Error: '; +//$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; +//$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; +//$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; +//$PHPMAILER_LANG['extension_missing'] = 'Extension missing: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-cs.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-cs.php new file mode 100644 index 0000000000..1160cf0cc4 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-cs.php @@ -0,0 +1,25 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP fejl: Kunne ikke logge på.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP fejl: Kunne ikke tilslutte SMTP serveren.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP fejl: Data kunne ikke accepteres.'; +//$PHPMAILER_LANG['empty_message'] = 'Message body empty'; +$PHPMAILER_LANG['encoding'] = 'Ukendt encode-format: '; +$PHPMAILER_LANG['execute'] = 'Kunne ikke køre: '; +$PHPMAILER_LANG['file_access'] = 'Ingen adgang til fil: '; +$PHPMAILER_LANG['file_open'] = 'Fil fejl: Kunne ikke åbne filen: '; +$PHPMAILER_LANG['from_failed'] = 'Følgende afsenderadresse er forkert: '; +$PHPMAILER_LANG['instantiate'] = 'Kunne ikke initialisere email funktionen.'; +//$PHPMAILER_LANG['invalid_address'] = 'Invalid address: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer understøttes ikke.'; +$PHPMAILER_LANG['provide_address'] = 'Du skal indtaste mindst en modtagers emailadresse.'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP fejl: Følgende modtagere er forkerte: '; +//$PHPMAILER_LANG['signing'] = 'Signing Error: '; +//$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; +//$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; +//$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; +//$PHPMAILER_LANG['extension_missing'] = 'Extension missing: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-de.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-de.php new file mode 100644 index 0000000000..aa987a9caf --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-de.php @@ -0,0 +1,25 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'Error SMTP: Imposible autentificar.'; +$PHPMAILER_LANG['connect_host'] = 'Error SMTP: Imposible conectar al servidor SMTP.'; +$PHPMAILER_LANG['data_not_accepted'] = 'Error SMTP: Datos no aceptados.'; +$PHPMAILER_LANG['empty_message'] = 'El cuerpo del mensaje está vacío.'; +$PHPMAILER_LANG['encoding'] = 'Codificación desconocida: '; +$PHPMAILER_LANG['execute'] = 'Imposible ejecutar: '; +$PHPMAILER_LANG['file_access'] = 'Imposible acceder al archivo: '; +$PHPMAILER_LANG['file_open'] = 'Error de Archivo: Imposible abrir el archivo: '; +$PHPMAILER_LANG['from_failed'] = 'La(s) siguiente(s) direcciones de remitente fallaron: '; +$PHPMAILER_LANG['instantiate'] = 'Imposible crear una instancia de la función Mail.'; +$PHPMAILER_LANG['invalid_address'] = 'Imposible enviar: dirección de email inválido: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer no está soportado.'; +$PHPMAILER_LANG['provide_address'] = 'Debe proporcionar al menos una dirección de email de destino.'; +$PHPMAILER_LANG['recipients_failed'] = 'Error SMTP: Los siguientes destinos fallaron: '; +$PHPMAILER_LANG['signing'] = 'Error al firmar: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() falló.'; +$PHPMAILER_LANG['smtp_error'] = 'Error del servidor SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'No se pudo configurar la variable: '; +$PHPMAILER_LANG['extension_missing'] = 'Extensión faltante: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-et.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-et.php new file mode 100644 index 0000000000..7e06da13e6 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-et.php @@ -0,0 +1,27 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP Viga: Autoriseerimise viga.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP Viga: Ei õnnestunud luua ühendust SMTP serveriga.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP Viga: Vigased andmed.'; +$PHPMAILER_LANG['empty_message'] = 'Tühi kirja sisu'; +$PHPMAILER_LANG["encoding"] = 'Tundmatu kodeering: '; +$PHPMAILER_LANG['execute'] = 'Tegevus ebaõnnestus: '; +$PHPMAILER_LANG['file_access'] = 'Pole piisavalt õiguseid järgneva faili avamiseks: '; +$PHPMAILER_LANG['file_open'] = 'Faili Viga: Faili avamine ebaõnnestus: '; +$PHPMAILER_LANG['from_failed'] = 'Järgnev saatja e-posti aadress on vigane: '; +$PHPMAILER_LANG['instantiate'] = 'mail funktiooni käivitamine ebaõnnestus.'; +$PHPMAILER_LANG['invalid_address'] = 'Saatmine peatatud, e-posti address vigane: '; +$PHPMAILER_LANG['provide_address'] = 'Te peate määrama vähemalt ühe saaja e-posti aadressi.'; +$PHPMAILER_LANG['mailer_not_supported'] = ' maileri tugi puudub.'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP Viga: Järgnevate saajate e-posti aadressid on vigased: '; +$PHPMAILER_LANG["signing"] = 'Viga allkirjastamisel: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() ebaõnnestus.'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP serveri viga: '; +$PHPMAILER_LANG['variable_set'] = 'Ei õnnestunud määrata või lähtestada muutujat: '; +$PHPMAILER_LANG['extension_missing'] = 'Nõutud laiendus on puudu: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-fa.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-fa.php new file mode 100644 index 0000000000..8aa0ad2218 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-fa.php @@ -0,0 +1,27 @@ + + * @author Mohammad Hossein Mojtahedi + */ + +$PHPMAILER_LANG['authenticate'] = 'خطای SMTP: احراز هویت با شکست مواجه شد.'; +$PHPMAILER_LANG['connect_host'] = 'خطای SMTP: اتصال به سرور SMTP برقرار نشد.'; +$PHPMAILER_LANG['data_not_accepted'] = 'خطای SMTP: داده‌ها نا‌درست هستند.'; +$PHPMAILER_LANG['empty_message'] = 'بخش متن پیام خالی است.'; +$PHPMAILER_LANG['encoding'] = 'کد‌گذاری نا‌شناخته: '; +$PHPMAILER_LANG['execute'] = 'امکان اجرا وجود ندارد: '; +$PHPMAILER_LANG['file_access'] = 'امکان دسترسی به فایل وجود ندارد: '; +$PHPMAILER_LANG['file_open'] = 'خطای File: امکان بازکردن فایل وجود ندارد: '; +$PHPMAILER_LANG['from_failed'] = 'آدرس فرستنده اشتباه است: '; +$PHPMAILER_LANG['instantiate'] = 'امکان معرفی تابع ایمیل وجود ندارد.'; +$PHPMAILER_LANG['invalid_address'] = 'آدرس ایمیل معتبر نیست: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer پشتیبانی نمی‌شود.'; +$PHPMAILER_LANG['provide_address'] = 'باید حداقل یک آدرس گیرنده وارد کنید.'; +$PHPMAILER_LANG['recipients_failed'] = 'خطای SMTP: ارسال به آدرس گیرنده با خطا مواجه شد: '; +$PHPMAILER_LANG['signing'] = 'خطا در امضا: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'خطا در اتصال به SMTP.'; +$PHPMAILER_LANG['smtp_error'] = 'خطا در SMTP Server: '; +$PHPMAILER_LANG['variable_set'] = 'امکان ارسال یا ارسال مجدد متغیر‌ها وجود ندارد: '; +$PHPMAILER_LANG['extension_missing'] = 'افزونه موجود نیست: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-fi.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-fi.php new file mode 100644 index 0000000000..ec4e752349 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-fi.php @@ -0,0 +1,27 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP feilur: Kundi ikki góðkenna.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP feilur: Kundi ikki knýta samband við SMTP vert.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP feilur: Data ikki góðkent.'; +//$PHPMAILER_LANG['empty_message'] = 'Message body empty'; +$PHPMAILER_LANG['encoding'] = 'Ókend encoding: '; +$PHPMAILER_LANG['execute'] = 'Kundi ikki útføra: '; +$PHPMAILER_LANG['file_access'] = 'Kundi ikki tilganga fílu: '; +$PHPMAILER_LANG['file_open'] = 'Fílu feilur: Kundi ikki opna fílu: '; +$PHPMAILER_LANG['from_failed'] = 'fylgjandi Frá/From adressa miseydnaðist: '; +$PHPMAILER_LANG['instantiate'] = 'Kuni ikki instantiera mail funktión.'; +//$PHPMAILER_LANG['invalid_address'] = 'Invalid address: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' er ikki supporterað.'; +$PHPMAILER_LANG['provide_address'] = 'Tú skal uppgeva minst móttakara-emailadressu(r).'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP Feilur: Fylgjandi móttakarar miseydnaðust: '; +//$PHPMAILER_LANG['signing'] = 'Signing Error: '; +//$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; +//$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; +//$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; +//$PHPMAILER_LANG['extension_missing'] = 'Extension missing: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-fr.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-fr.php new file mode 100644 index 0000000000..af68c92368 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-fr.php @@ -0,0 +1,29 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'Erro SMTP: Non puido ser autentificado.'; +$PHPMAILER_LANG['connect_host'] = 'Erro SMTP: Non puido conectar co servidor SMTP.'; +$PHPMAILER_LANG['data_not_accepted'] = 'Erro SMTP: Datos non aceptados.'; +$PHPMAILER_LANG['empty_message'] = 'Corpo da mensaxe vacía'; +$PHPMAILER_LANG['encoding'] = 'Codificación descoñecida: '; +$PHPMAILER_LANG['execute'] = 'Non puido ser executado: '; +$PHPMAILER_LANG['file_access'] = 'Nob puido acceder ó arquivo: '; +$PHPMAILER_LANG['file_open'] = 'Erro de Arquivo: No puido abrir o arquivo: '; +$PHPMAILER_LANG['from_failed'] = 'A(s) seguinte(s) dirección(s) de remitente(s) deron erro: '; +$PHPMAILER_LANG['instantiate'] = 'Non puido crear unha instancia da función Mail.'; +$PHPMAILER_LANG['invalid_address'] = 'Non puido envia-lo correo: dirección de email inválida: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer non está soportado.'; +$PHPMAILER_LANG['provide_address'] = 'Debe engadir polo menos unha dirección de email coma destino.'; +$PHPMAILER_LANG['recipients_failed'] = 'Erro SMTP: Os seguintes destinos fallaron: '; +$PHPMAILER_LANG['signing'] = 'Erro ó firmar: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() fallou.'; +$PHPMAILER_LANG['smtp_error'] = 'Erro do servidor SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'Non puidemos axustar ou reaxustar a variábel: '; +//$PHPMAILER_LANG['extension_missing'] = 'Extension missing: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-he.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-he.php new file mode 100644 index 0000000000..70eb717578 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-he.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'שגיאת SMTP: פעולת האימות נכשלה.'; +$PHPMAILER_LANG['connect_host'] = 'שגיאת SMTP: לא הצלחתי להתחבר לשרת SMTP.'; +$PHPMAILER_LANG['data_not_accepted'] = 'שגיאת SMTP: מידע לא התקבל.'; +$PHPMAILER_LANG['empty_message'] = 'גוף ההודעה ריק'; +$PHPMAILER_LANG['invalid_address'] = 'כתובת שגויה: '; +$PHPMAILER_LANG['encoding'] = 'קידוד לא מוכר: '; +$PHPMAILER_LANG['execute'] = 'לא הצלחתי להפעיל את: '; +$PHPMAILER_LANG['file_access'] = 'לא ניתן לגשת לקובץ: '; +$PHPMAILER_LANG['file_open'] = 'שגיאת קובץ: לא ניתן לגשת לקובץ: '; +$PHPMAILER_LANG['from_failed'] = 'כתובות הנמענים הבאות נכשלו: '; +$PHPMAILER_LANG['instantiate'] = 'לא הצלחתי להפעיל את פונקציית המייל.'; +$PHPMAILER_LANG['mailer_not_supported'] = ' אינה נתמכת.'; +$PHPMAILER_LANG['provide_address'] = 'חובה לספק לפחות כתובת אחת של מקבל המייל.'; +$PHPMAILER_LANG['recipients_failed'] = 'שגיאת SMTP: הנמענים הבאים נכשלו: '; +$PHPMAILER_LANG['signing'] = 'שגיאת חתימה: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; +$PHPMAILER_LANG['smtp_error'] = 'שגיאת שרת SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'לא ניתן לקבוע או לשנות את המשתנה: '; +//$PHPMAILER_LANG['extension_missing'] = 'Extension missing: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-hi.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-hi.php new file mode 100644 index 0000000000..607a5ee3f9 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-hi.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP त्रुटि: प्रामाणिकता की जांच नहीं हो सका। '; +$PHPMAILER_LANG['connect_host'] = 'SMTP त्रुटि: SMTP सर्वर से कनेक्ट नहीं हो सका। '; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP त्रुटि: डेटा स्वीकार नहीं किया जाता है। '; +$PHPMAILER_LANG['empty_message'] = 'संदेश खाली है। '; +$PHPMAILER_LANG['encoding'] = 'अज्ञात एन्कोडिंग प्रकार। '; +$PHPMAILER_LANG['execute'] = 'आदेश को निष्पादित करने में विफल। '; +$PHPMAILER_LANG['file_access'] = 'फ़ाइल उपलब्ध नहीं है। '; +$PHPMAILER_LANG['file_open'] = 'फ़ाइल त्रुटि: फाइल को खोला नहीं जा सका। '; +$PHPMAILER_LANG['from_failed'] = 'प्रेषक का पता गलत है। '; +$PHPMAILER_LANG['instantiate'] = 'मेल फ़ंक्शन कॉल नहीं कर सकता है।'; +$PHPMAILER_LANG['invalid_address'] = 'पता गलत है। '; +$PHPMAILER_LANG['mailer_not_supported'] = 'मेल सर्वर के साथ काम नहीं करता है। '; +$PHPMAILER_LANG['provide_address'] = 'आपको कम से कम एक प्राप्तकर्ता का ई-मेल पता प्रदान करना होगा।'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP त्रुटि: निम्न प्राप्तकर्ताओं को पते भेजने में विफल। '; +$PHPMAILER_LANG['signing'] = 'साइनअप त्रुटि:। '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP का connect () फ़ंक्शन विफल हुआ। '; +$PHPMAILER_LANG['smtp_error'] = 'SMTP सर्वर त्रुटि। '; +$PHPMAILER_LANG['variable_set'] = 'चर को बना या संशोधित नहीं किया जा सकता। '; +$PHPMAILER_LANG['extension_missing'] = 'एक्सटेन्षन गायब है: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-hr.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-hr.php new file mode 100644 index 0000000000..3822920add --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-hr.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP Greška: Neuspjela autentikacija.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP Greška: Ne mogu se spojiti na SMTP poslužitelj.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP Greška: Podatci nisu prihvaćeni.'; +$PHPMAILER_LANG['empty_message'] = 'Sadržaj poruke je prazan.'; +$PHPMAILER_LANG['encoding'] = 'Nepoznati encoding: '; +$PHPMAILER_LANG['execute'] = 'Nije moguće izvršiti naredbu: '; +$PHPMAILER_LANG['file_access'] = 'Nije moguće pristupiti datoteci: '; +$PHPMAILER_LANG['file_open'] = 'Nije moguće otvoriti datoteku: '; +$PHPMAILER_LANG['from_failed'] = 'SMTP Greška: Slanje s navedenih e-mail adresa nije uspjelo: '; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP Greška: Slanje na navedenih e-mail adresa nije uspjelo: '; +$PHPMAILER_LANG['instantiate'] = 'Ne mogu pokrenuti mail funkcionalnost.'; +$PHPMAILER_LANG['invalid_address'] = 'E-mail nije poslan. Neispravna e-mail adresa: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer nije podržan.'; +$PHPMAILER_LANG['provide_address'] = 'Definirajte barem jednu adresu primatelja.'; +$PHPMAILER_LANG['signing'] = 'Greška prilikom prijave: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Spajanje na SMTP poslužitelj nije uspjelo.'; +$PHPMAILER_LANG['smtp_error'] = 'Greška SMTP poslužitelja: '; +$PHPMAILER_LANG['variable_set'] = 'Ne mogu postaviti varijablu niti ju vratiti nazad: '; +$PHPMAILER_LANG['extension_missing'] = 'Nedostaje proširenje: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-hu.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-hu.php new file mode 100644 index 0000000000..196cddc224 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-hu.php @@ -0,0 +1,26 @@ + + * @author @januridp + */ + +$PHPMAILER_LANG['authenticate'] = 'Kesalahan SMTP: Tidak dapat mengotentikasi.'; +$PHPMAILER_LANG['connect_host'] = 'Kesalahan SMTP: Tidak dapat terhubung ke host SMTP.'; +$PHPMAILER_LANG['data_not_accepted'] = 'Kesalahan SMTP: Data tidak diterima.'; +$PHPMAILER_LANG['empty_message'] = 'Isi pesan kosong'; +$PHPMAILER_LANG['encoding'] = 'Pengkodean karakter tidak dikenali: '; +$PHPMAILER_LANG['execute'] = 'Tidak dapat menjalankan proses : '; +$PHPMAILER_LANG['file_access'] = 'Tidak dapat mengakses berkas : '; +$PHPMAILER_LANG['file_open'] = 'Kesalahan File: Berkas tidak dapat dibuka : '; +$PHPMAILER_LANG['from_failed'] = 'Alamat pengirim berikut mengakibatkan kesalahan : '; +$PHPMAILER_LANG['instantiate'] = 'Tidak dapat menginisialisasi fungsi surel'; +$PHPMAILER_LANG['invalid_address'] = 'Gagal terkirim, alamat surel tidak benar : '; +$PHPMAILER_LANG['provide_address'] = 'Harus disediakan minimal satu alamat tujuan'; +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer tidak didukung'; +$PHPMAILER_LANG['recipients_failed'] = 'Kesalahan SMTP: Alamat tujuan berikut menghasilkan kesalahan : '; +$PHPMAILER_LANG['signing'] = 'Kesalahan dalam tanda tangan : '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() gagal.'; +$PHPMAILER_LANG['smtp_error'] = 'Kesalahan pada pelayan SMTP : '; +$PHPMAILER_LANG['variable_set'] = 'Tidak dapat mengatur atau mengatur ulang variable : '; +$PHPMAILER_LANG['extension_missing'] = 'Ekstensi hilang: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-it.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-it.php new file mode 100644 index 0000000000..e67b6f72c6 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-it.php @@ -0,0 +1,27 @@ + + * @author Stefano Sabatini + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP Error: Impossibile autenticarsi.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP Error: Impossibile connettersi all\'host SMTP.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP Error: Dati non accettati dal server.'; +$PHPMAILER_LANG['empty_message'] = 'Il corpo del messaggio è vuoto'; +$PHPMAILER_LANG['encoding'] = 'Codifica dei caratteri sconosciuta: '; +$PHPMAILER_LANG['execute'] = 'Impossibile eseguire l\'operazione: '; +$PHPMAILER_LANG['file_access'] = 'Impossibile accedere al file: '; +$PHPMAILER_LANG['file_open'] = 'File Error: Impossibile aprire il file: '; +$PHPMAILER_LANG['from_failed'] = 'I seguenti indirizzi mittenti hanno generato errore: '; +$PHPMAILER_LANG['instantiate'] = 'Impossibile istanziare la funzione mail'; +$PHPMAILER_LANG['invalid_address'] = 'Impossibile inviare, l\'indirizzo email non è valido: '; +$PHPMAILER_LANG['provide_address'] = 'Deve essere fornito almeno un indirizzo ricevente'; +$PHPMAILER_LANG['mailer_not_supported'] = 'Mailer non supportato'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP Error: I seguenti indirizzi destinatari hanno generato un errore: '; +$PHPMAILER_LANG['signing'] = 'Errore nella firma: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() fallita.'; +$PHPMAILER_LANG['smtp_error'] = 'Errore del server SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'Impossibile impostare o resettare la variabile: '; +$PHPMAILER_LANG['extension_missing'] = 'Estensione mancante: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-ja.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ja.php new file mode 100644 index 0000000000..2d778728d7 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ja.php @@ -0,0 +1,27 @@ + + * @author Yoshi Sakai + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTPエラー: 認証できませんでした。'; +$PHPMAILER_LANG['connect_host'] = 'SMTPエラー: SMTPホストに接続できませんでした。'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTPエラー: データが受け付けられませんでした。'; +//$PHPMAILER_LANG['empty_message'] = 'Message body empty'; +$PHPMAILER_LANG['encoding'] = '不明なエンコーディング: '; +$PHPMAILER_LANG['execute'] = '実行できませんでした: '; +$PHPMAILER_LANG['file_access'] = 'ファイルにアクセスできません: '; +$PHPMAILER_LANG['file_open'] = 'ファイルエラー: ファイルを開けません: '; +$PHPMAILER_LANG['from_failed'] = 'Fromアドレスを登録する際にエラーが発生しました: '; +$PHPMAILER_LANG['instantiate'] = 'メール関数が正常に動作しませんでした。'; +//$PHPMAILER_LANG['invalid_address'] = 'Invalid address: '; +$PHPMAILER_LANG['provide_address'] = '少なくとも1つメールアドレスを 指定する必要があります。'; +$PHPMAILER_LANG['mailer_not_supported'] = ' メーラーがサポートされていません。'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTPエラー: 次の受信者アドレスに 間違いがあります: '; +//$PHPMAILER_LANG['signing'] = 'Signing Error: '; +//$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() failed.'; +//$PHPMAILER_LANG['smtp_error'] = 'SMTP server error: '; +//$PHPMAILER_LANG['variable_set'] = 'Cannot set or reset variable: '; +//$PHPMAILER_LANG['extension_missing'] = 'Extension missing: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-ka.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ka.php new file mode 100644 index 0000000000..dd1af8abec --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ka.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP შეცდომა: ავტორიზაცია შეუძლებელია.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP შეცდომა: SMTP სერვერთან დაკავშირება შეუძლებელია.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP შეცდომა: მონაცემები არ იქნა მიღებული.'; +$PHPMAILER_LANG['encoding'] = 'კოდირების უცნობი ტიპი: '; +$PHPMAILER_LANG['execute'] = 'შეუძლებელია შემდეგი ბრძანების შესრულება: '; +$PHPMAILER_LANG['file_access'] = 'შეუძლებელია წვდომა ფაილთან: '; +$PHPMAILER_LANG['file_open'] = 'ფაილური სისტემის შეცდომა: არ იხსნება ფაილი: '; +$PHPMAILER_LANG['from_failed'] = 'გამგზავნის არასწორი მისამართი: '; +$PHPMAILER_LANG['instantiate'] = 'mail ფუნქციის გაშვება ვერ ხერხდება.'; +$PHPMAILER_LANG['provide_address'] = 'გთხოვთ მიუთითოთ ერთი ადრესატის e-mail მისამართი მაინც.'; +$PHPMAILER_LANG['mailer_not_supported'] = ' - საფოსტო სერვერის მხარდაჭერა არ არის.'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP შეცდომა: შემდეგ მისამართებზე გაგზავნა ვერ მოხერხდა: '; +$PHPMAILER_LANG['empty_message'] = 'შეტყობინება ცარიელია'; +$PHPMAILER_LANG['invalid_address'] = 'არ გაიგზავნა, e-mail მისამართის არასწორი ფორმატი: '; +$PHPMAILER_LANG['signing'] = 'ხელმოწერის შეცდომა: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'შეცდომა SMTP სერვერთან დაკავშირებისას'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP სერვერის შეცდომა: '; +$PHPMAILER_LANG['variable_set'] = 'შეუძლებელია შემდეგი ცვლადის შექმნა ან შეცვლა: '; +$PHPMAILER_LANG['extension_missing'] = 'ბიბლიოთეკა არ არსებობს: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-ko.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ko.php new file mode 100644 index 0000000000..9599fa6819 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ko.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP 오류: 인증할 수 없습니다.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP 오류: SMTP 호스트에 접속할 수 없습니다.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP 오류: 데이터가 받아들여지지 않았습니다.'; +$PHPMAILER_LANG['empty_message'] = '메세지 내용이 없습니다'; +$PHPMAILER_LANG['encoding'] = '알 수 없는 인코딩: '; +$PHPMAILER_LANG['execute'] = '실행 불가: '; +$PHPMAILER_LANG['file_access'] = '파일 접근 불가: '; +$PHPMAILER_LANG['file_open'] = '파일 오류: 파일을 열 수 없습니다: '; +$PHPMAILER_LANG['from_failed'] = '다음 From 주소에서 오류가 발생했습니다: '; +$PHPMAILER_LANG['instantiate'] = 'mail 함수를 인스턴스화할 수 없습니다'; +$PHPMAILER_LANG['invalid_address'] = '잘못된 주소: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' 메일러는 지원되지 않습니다.'; +$PHPMAILER_LANG['provide_address'] = '적어도 한 개 이상의 수신자 메일 주소를 제공해야 합니다.'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP 오류: 다음 수신자에서 오류가 발생했습니다: '; +$PHPMAILER_LANG['signing'] = '서명 오류: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP 연결을 실패하였습니다.'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP 서버 오류: '; +$PHPMAILER_LANG['variable_set'] = '변수 설정 및 초기화 불가: '; +$PHPMAILER_LANG['extension_missing'] = '확장자 없음: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-lt.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-lt.php new file mode 100644 index 0000000000..1253a4fdb4 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-lt.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP klaida: autentifikacija nepavyko.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP klaida: nepavyksta prisijungti prie SMTP stoties.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP klaida: duomenys nepriimti.'; +$PHPMAILER_LANG['empty_message'] = 'Laiško turinys tuščias'; +$PHPMAILER_LANG['encoding'] = 'Neatpažinta koduotė: '; +$PHPMAILER_LANG['execute'] = 'Nepavyko įvykdyti komandos: '; +$PHPMAILER_LANG['file_access'] = 'Byla nepasiekiama: '; +$PHPMAILER_LANG['file_open'] = 'Bylos klaida: Nepavyksta atidaryti: '; +$PHPMAILER_LANG['from_failed'] = 'Neteisingas siuntėjo adresas: '; +$PHPMAILER_LANG['instantiate'] = 'Nepavyko paleisti mail funkcijos.'; +$PHPMAILER_LANG['invalid_address'] = 'Neteisingas adresas: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' pašto stotis nepalaikoma.'; +$PHPMAILER_LANG['provide_address'] = 'Nurodykite bent vieną gavėjo adresą.'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP klaida: nepavyko išsiųsti šiems gavėjams: '; +$PHPMAILER_LANG['signing'] = 'Prisijungimo klaida: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP susijungimo klaida'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP stoties klaida: '; +$PHPMAILER_LANG['variable_set'] = 'Nepavyko priskirti reikšmės kintamajam: '; +//$PHPMAILER_LANG['extension_missing'] = 'Extension missing: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-lv.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-lv.php new file mode 100644 index 0000000000..39bf9a19e2 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-lv.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP kļūda: Autorizācija neizdevās.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP Kļūda: Nevar izveidot savienojumu ar SMTP serveri.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP Kļūda: Nepieņem informāciju.'; +$PHPMAILER_LANG['empty_message'] = 'Ziņojuma teksts ir tukšs'; +$PHPMAILER_LANG['encoding'] = 'Neatpazīts kodējums: '; +$PHPMAILER_LANG['execute'] = 'Neizdevās izpildīt komandu: '; +$PHPMAILER_LANG['file_access'] = 'Fails nav pieejams: '; +$PHPMAILER_LANG['file_open'] = 'Faila kļūda: Nevar atvērt failu: '; +$PHPMAILER_LANG['from_failed'] = 'Nepareiza sūtītāja adrese: '; +$PHPMAILER_LANG['instantiate'] = 'Nevar palaist sūtīšanas funkciju.'; +$PHPMAILER_LANG['invalid_address'] = 'Nepareiza adrese: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' sūtītājs netiek atbalstīts.'; +$PHPMAILER_LANG['provide_address'] = 'Lūdzu, norādiet vismaz vienu adresātu.'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP kļūda: neizdevās nosūtīt šādiem saņēmējiem: '; +$PHPMAILER_LANG['signing'] = 'Autorizācijas kļūda: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP savienojuma kļūda'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP servera kļūda: '; +$PHPMAILER_LANG['variable_set'] = 'Nevar piešķirt mainīgā vērtību: '; +//$PHPMAILER_LANG['extension_missing'] = 'Extension missing: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-mg.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-mg.php new file mode 100644 index 0000000000..f4c7563030 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-mg.php @@ -0,0 +1,25 @@ + + */ +$PHPMAILER_LANG['authenticate'] = 'Hadisoana SMTP: Tsy nahomby ny fanamarinana.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP Error: Tsy afaka mampifandray amin\'ny mpampiantrano SMTP.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP diso: tsy voarakitra ny angona.'; +$PHPMAILER_LANG['empty_message'] = 'Tsy misy ny votoaty mailaka.'; +$PHPMAILER_LANG['encoding'] = 'Tsy fantatra encoding: '; +$PHPMAILER_LANG['execute'] = 'Tsy afaka manatanteraka ity baiko manaraka ity: '; +$PHPMAILER_LANG['file_access'] = 'Tsy nahomby ny fidirana amin\'ity rakitra ity: '; +$PHPMAILER_LANG['file_open'] = 'Hadisoana diso: Tsy afaka nanokatra ity file manaraka ity: '; +$PHPMAILER_LANG['from_failed'] = 'Ny adiresy iraka manaraka dia diso: '; +$PHPMAILER_LANG['instantiate'] = 'Tsy afaka nanomboka ny hetsika mail.'; +$PHPMAILER_LANG['invalid_address'] = 'Tsy mety ny adiresy: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer tsy manohana.'; +$PHPMAILER_LANG['provide_address'] = 'Alefaso azafady iray adiresy iray farafahakeliny.'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP Error: Tsy mety ireo mpanaraka ireto: '; +$PHPMAILER_LANG['signing'] = 'Error nandritra ny sonia:'; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Tsy nahomby ny fifandraisana tamin\'ny server SMTP.'; +$PHPMAILER_LANG['smtp_error'] = 'Fahadisoana tamin\'ny server SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'Tsy azo atao ny mametraka na mamerina ny variable: '; +$PHPMAILER_LANG['extension_missing'] = 'Tsy hita ny ampahany: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-ms.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ms.php new file mode 100644 index 0000000000..f12a6ad486 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ms.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'Ralat SMTP: Tidak dapat pengesahan.'; +$PHPMAILER_LANG['connect_host'] = 'Ralat SMTP: Tidak dapat menghubungi hos pelayan SMTP.'; +$PHPMAILER_LANG['data_not_accepted'] = 'Ralat SMTP: Data tidak diterima oleh pelayan.'; +$PHPMAILER_LANG['empty_message'] = 'Tiada isi untuk mesej'; +$PHPMAILER_LANG['encoding'] = 'Pengekodan tidak diketahui: '; +$PHPMAILER_LANG['execute'] = 'Tidak dapat melaksanakan: '; +$PHPMAILER_LANG['file_access'] = 'Tidak dapat mengakses fail: '; +$PHPMAILER_LANG['file_open'] = 'Ralat Fail: Tidak dapat membuka fail: '; +$PHPMAILER_LANG['from_failed'] = 'Berikut merupakan ralat dari alamat e-mel: '; +$PHPMAILER_LANG['instantiate'] = 'Tidak dapat memberi contoh fungsi e-mel.'; +$PHPMAILER_LANG['invalid_address'] = 'Alamat emel tidak sah: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' jenis penghantar emel tidak disokong.'; +$PHPMAILER_LANG['provide_address'] = 'Anda perlu menyediakan sekurang-kurangnya satu alamat e-mel penerima.'; +$PHPMAILER_LANG['recipients_failed'] = 'Ralat SMTP: Penerima e-mel berikut telah gagal: '; +$PHPMAILER_LANG['signing'] = 'Ralat pada tanda tangan: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() telah gagal.'; +$PHPMAILER_LANG['smtp_error'] = 'Ralat pada pelayan SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'Tidak boleh menetapkan atau menetapkan semula pembolehubah: '; +$PHPMAILER_LANG['extension_missing'] = 'Sambungan hilang: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-nb.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-nb.php new file mode 100644 index 0000000000..97403e73ca --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-nb.php @@ -0,0 +1,25 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP-fout: authenticatie mislukt.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP-fout: kon niet verbinden met SMTP-host.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP-fout: data niet geaccepteerd.'; +$PHPMAILER_LANG['empty_message'] = 'Berichttekst is leeg'; +$PHPMAILER_LANG['encoding'] = 'Onbekende codering: '; +$PHPMAILER_LANG['execute'] = 'Kon niet uitvoeren: '; +$PHPMAILER_LANG['file_access'] = 'Kreeg geen toegang tot bestand: '; +$PHPMAILER_LANG['file_open'] = 'Bestandsfout: kon bestand niet openen: '; +$PHPMAILER_LANG['from_failed'] = 'Het volgende afzendersadres is mislukt: '; +$PHPMAILER_LANG['instantiate'] = 'Kon mailfunctie niet initialiseren.'; +$PHPMAILER_LANG['invalid_address'] = 'Ongeldig adres: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer wordt niet ondersteund.'; +$PHPMAILER_LANG['provide_address'] = 'Er moet minstens één ontvanger worden opgegeven.'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP-fout: de volgende ontvangers zijn mislukt: '; +$PHPMAILER_LANG['signing'] = 'Signeerfout: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Verbinding mislukt.'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP-serverfout: '; +$PHPMAILER_LANG['variable_set'] = 'Kan de volgende variabele niet instellen of resetten: '; +$PHPMAILER_LANG['extension_missing'] = 'Extensie afwezig: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-pl.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-pl.php new file mode 100644 index 0000000000..3da0dee91f --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-pl.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'Erro do SMTP: Não foi possível realizar a autenticação.'; +$PHPMAILER_LANG['connect_host'] = 'Erro do SMTP: Não foi possível realizar ligação com o servidor SMTP.'; +$PHPMAILER_LANG['data_not_accepted'] = 'Erro do SMTP: Os dados foram rejeitados.'; +$PHPMAILER_LANG['empty_message'] = 'A mensagem no e-mail está vazia.'; +$PHPMAILER_LANG['encoding'] = 'Codificação desconhecida: '; +$PHPMAILER_LANG['execute'] = 'Não foi possível executar: '; +$PHPMAILER_LANG['file_access'] = 'Não foi possível aceder o ficheiro: '; +$PHPMAILER_LANG['file_open'] = 'Abertura do ficheiro: Não foi possível abrir o ficheiro: '; +$PHPMAILER_LANG['from_failed'] = 'Ocorreram falhas nos endereços dos seguintes remententes: '; +$PHPMAILER_LANG['instantiate'] = 'Não foi possível iniciar uma instância da função mail.'; +$PHPMAILER_LANG['invalid_address'] = 'Não foi enviado nenhum e-mail para o endereço de e-mail inválido: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer não é suportado.'; +$PHPMAILER_LANG['provide_address'] = 'Tem de fornecer pelo menos um endereço como destinatário do e-mail.'; +$PHPMAILER_LANG['recipients_failed'] = 'Erro do SMTP: O endereço do seguinte destinatário falhou: '; +$PHPMAILER_LANG['signing'] = 'Erro ao assinar: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() falhou.'; +$PHPMAILER_LANG['smtp_error'] = 'Erro de servidor SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'Não foi possível definir ou redefinir a variável: '; +$PHPMAILER_LANG['extension_missing'] = 'Extensão em falta: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-pt_br.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-pt_br.php new file mode 100644 index 0000000000..62d692d426 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-pt_br.php @@ -0,0 +1,29 @@ + + * @author Lucas Guimarães + * @author Phelipe Alves + * @author Fabio Beneditto + */ + +$PHPMAILER_LANG['authenticate'] = 'Erro de SMTP: Não foi possível autenticar.'; +$PHPMAILER_LANG['connect_host'] = 'Erro de SMTP: Não foi possível conectar ao servidor SMTP.'; +$PHPMAILER_LANG['data_not_accepted'] = 'Erro de SMTP: Dados rejeitados.'; +$PHPMAILER_LANG['empty_message'] = 'Mensagem vazia'; +$PHPMAILER_LANG['encoding'] = 'Codificação desconhecida: '; +$PHPMAILER_LANG['execute'] = 'Não foi possível executar: '; +$PHPMAILER_LANG['file_access'] = 'Não foi possível acessar o arquivo: '; +$PHPMAILER_LANG['file_open'] = 'Erro de Arquivo: Não foi possível abrir o arquivo: '; +$PHPMAILER_LANG['from_failed'] = 'Os seguintes remetentes falharam: '; +$PHPMAILER_LANG['instantiate'] = 'Não foi possível instanciar a função mail.'; +$PHPMAILER_LANG['invalid_address'] = 'Endereço de e-mail inválido: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer não é suportado.'; +$PHPMAILER_LANG['provide_address'] = 'Você deve informar pelo menos um destinatário.'; +$PHPMAILER_LANG['recipients_failed'] = 'Erro de SMTP: Os seguintes destinatários falharam: '; +$PHPMAILER_LANG['signing'] = 'Erro de Assinatura: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() falhou.'; +$PHPMAILER_LANG['smtp_error'] = 'Erro de servidor SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'Não foi possível definir ou redefinir a variável: '; +$PHPMAILER_LANG['extension_missing'] = 'Extensão não existe: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-ro.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ro.php new file mode 100644 index 0000000000..fa100eaa20 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ro.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'Eroare SMTP: Autentificarea a eșuat.'; +$PHPMAILER_LANG['connect_host'] = 'Eroare SMTP: Conectarea la serverul SMTP a eșuat.'; +$PHPMAILER_LANG['data_not_accepted'] = 'Eroare SMTP: Datele nu au fost acceptate.'; +$PHPMAILER_LANG['empty_message'] = 'Mesajul este gol.'; +$PHPMAILER_LANG['encoding'] = 'Encodare necunoscută: '; +$PHPMAILER_LANG['execute'] = 'Nu se poate executa următoarea comandă: '; +$PHPMAILER_LANG['file_access'] = 'Nu se poate accesa următorul fișier: '; +$PHPMAILER_LANG['file_open'] = 'Eroare fișier: Nu se poate deschide următorul fișier: '; +$PHPMAILER_LANG['from_failed'] = 'Următoarele adrese From au dat eroare: '; +$PHPMAILER_LANG['instantiate'] = 'Funcția mail nu a putut fi inițializată.'; +$PHPMAILER_LANG['invalid_address'] = 'Adresa de email nu este validă: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer nu este suportat.'; +$PHPMAILER_LANG['provide_address'] = 'Trebuie să adăugați cel puțin o adresă de email.'; +$PHPMAILER_LANG['recipients_failed'] = 'Eroare SMTP: Următoarele adrese de email au eșuat: '; +$PHPMAILER_LANG['signing'] = 'A aparut o problemă la semnarea emailului. '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Conectarea la serverul SMTP a eșuat.'; +$PHPMAILER_LANG['smtp_error'] = 'Eroare server SMTP: '; +$PHPMAILER_LANG['variable_set'] = 'Nu se poate seta/reseta variabila. '; +$PHPMAILER_LANG['extension_missing'] = 'Lipsește extensia: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-ru.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ru.php new file mode 100644 index 0000000000..720e9a116d --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-ru.php @@ -0,0 +1,27 @@ + + * @author Foster Snowhill + */ + +$PHPMAILER_LANG['authenticate'] = 'Ошибка SMTP: ошибка авторизации.'; +$PHPMAILER_LANG['connect_host'] = 'Ошибка SMTP: не удается подключиться к SMTP-серверу.'; +$PHPMAILER_LANG['data_not_accepted'] = 'Ошибка SMTP: данные не приняты.'; +$PHPMAILER_LANG['encoding'] = 'Неизвестная кодировка: '; +$PHPMAILER_LANG['execute'] = 'Невозможно выполнить команду: '; +$PHPMAILER_LANG['file_access'] = 'Нет доступа к файлу: '; +$PHPMAILER_LANG['file_open'] = 'Файловая ошибка: не удаётся открыть файл: '; +$PHPMAILER_LANG['from_failed'] = 'Неверный адрес отправителя: '; +$PHPMAILER_LANG['instantiate'] = 'Невозможно запустить функцию mail().'; +$PHPMAILER_LANG['provide_address'] = 'Пожалуйста, введите хотя бы один email-адрес получателя.'; +$PHPMAILER_LANG['mailer_not_supported'] = ' — почтовый сервер не поддерживается.'; +$PHPMAILER_LANG['recipients_failed'] = 'Ошибка SMTP: не удалась отправка таким адресатам: '; +$PHPMAILER_LANG['empty_message'] = 'Пустое сообщение'; +$PHPMAILER_LANG['invalid_address'] = 'Не отправлено из-за неправильного формата email-адреса: '; +$PHPMAILER_LANG['signing'] = 'Ошибка подписи: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Ошибка соединения с SMTP-сервером'; +$PHPMAILER_LANG['smtp_error'] = 'Ошибка SMTP-сервера: '; +$PHPMAILER_LANG['variable_set'] = 'Невозможно установить или сбросить переменную: '; +$PHPMAILER_LANG['extension_missing'] = 'Расширение отсутствует: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-sk.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-sk.php new file mode 100644 index 0000000000..69cfb0fc1d --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-sk.php @@ -0,0 +1,27 @@ + + * @author Peter Orlický + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP Error: Chyba autentifikácie.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP Error: Nebolo možné nadviazať spojenie so SMTP serverom.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP Error: Dáta neboli prijaté'; +$PHPMAILER_LANG['empty_message'] = 'Prázdne telo správy.'; +$PHPMAILER_LANG['encoding'] = 'Neznáme kódovanie: '; +$PHPMAILER_LANG['execute'] = 'Nedá sa vykonať: '; +$PHPMAILER_LANG['file_access'] = 'Súbor nebol nájdený: '; +$PHPMAILER_LANG['file_open'] = 'File Error: Súbor sa otvoriť pre čítanie: '; +$PHPMAILER_LANG['from_failed'] = 'Následujúca adresa From je nesprávna: '; +$PHPMAILER_LANG['instantiate'] = 'Nedá sa vytvoriť inštancia emailovej funkcie.'; +$PHPMAILER_LANG['invalid_address'] = 'Neodoslané, emailová adresa je nesprávna: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' emailový klient nieje podporovaný.'; +$PHPMAILER_LANG['provide_address'] = 'Musíte zadať aspoň jednu emailovú adresu príjemcu.'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP Error: Adresy príjemcov niesu správne '; +$PHPMAILER_LANG['signing'] = 'Chyba prihlasovania: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() zlyhalo.'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP chyba serveru: '; +$PHPMAILER_LANG['variable_set'] = 'Nemožno nastaviť alebo resetovať premennú: '; +$PHPMAILER_LANG['extension_missing'] = 'Chýba rozšírenie: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-sl.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-sl.php new file mode 100644 index 0000000000..1e3cb7fa9b --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-sl.php @@ -0,0 +1,27 @@ + + * @author Filip Š + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP napaka: Avtentikacija ni uspela.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP napaka: Vzpostavljanje povezave s SMTP gostiteljem ni uspelo.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP napaka: Strežnik zavrača podatke.'; +$PHPMAILER_LANG['empty_message'] = 'E-poštno sporočilo nima vsebine.'; +$PHPMAILER_LANG['encoding'] = 'Nepoznan tip kodiranja: '; +$PHPMAILER_LANG['execute'] = 'Operacija ni uspela: '; +$PHPMAILER_LANG['file_access'] = 'Nimam dostopa do datoteke: '; +$PHPMAILER_LANG['file_open'] = 'Ne morem odpreti datoteke: '; +$PHPMAILER_LANG['from_failed'] = 'Neveljaven e-naslov pošiljatelja: '; +$PHPMAILER_LANG['instantiate'] = 'Ne morem inicializirati mail funkcije.'; +$PHPMAILER_LANG['invalid_address'] = 'E-poštno sporočilo ni bilo poslano. E-naslov je neveljaven: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer ni podprt.'; +$PHPMAILER_LANG['provide_address'] = 'Prosim vnesite vsaj enega naslovnika.'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP napaka: Sledeči naslovniki so neveljavni: '; +$PHPMAILER_LANG['signing'] = 'Napaka pri podpisovanju: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Ne morem vzpostaviti povezave s SMTP strežnikom.'; +$PHPMAILER_LANG['smtp_error'] = 'Napaka SMTP strežnika: '; +$PHPMAILER_LANG['variable_set'] = 'Ne morem nastaviti oz. ponastaviti spremenljivke: '; +$PHPMAILER_LANG['extension_missing'] = 'Manjkajoča razširitev: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-sr.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-sr.php new file mode 100644 index 0000000000..34c1e182a0 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-sr.php @@ -0,0 +1,27 @@ + + * @author Miloš Milanović + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP грешка: аутентификација није успела.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP грешка: повезивање са SMTP сервером није успело.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP грешка: подаци нису прихваћени.'; +$PHPMAILER_LANG['empty_message'] = 'Садржај поруке је празан.'; +$PHPMAILER_LANG['encoding'] = 'Непознато кодирање: '; +$PHPMAILER_LANG['execute'] = 'Није могуће извршити наредбу: '; +$PHPMAILER_LANG['file_access'] = 'Није могуће приступити датотеци: '; +$PHPMAILER_LANG['file_open'] = 'Није могуће отворити датотеку: '; +$PHPMAILER_LANG['from_failed'] = 'SMTP грешка: слање са следећих адреса није успело: '; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP грешка: слање на следеће адресе није успело: '; +$PHPMAILER_LANG['instantiate'] = 'Није могуће покренути mail функцију.'; +$PHPMAILER_LANG['invalid_address'] = 'Порука није послата. Неисправна адреса: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' мејлер није подржан.'; +$PHPMAILER_LANG['provide_address'] = 'Дефинишите бар једну адресу примаоца.'; +$PHPMAILER_LANG['signing'] = 'Грешка приликом пријаве: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Повезивање са SMTP сервером није успело.'; +$PHPMAILER_LANG['smtp_error'] = 'Грешка SMTP сервера: '; +$PHPMAILER_LANG['variable_set'] = 'Није могуће задати нити ресетовати променљиву: '; +$PHPMAILER_LANG['extension_missing'] = 'Недостаје проширење: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-sv.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-sv.php new file mode 100644 index 0000000000..4408e63eb0 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-sv.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP fel: Kunde inte autentisera.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP fel: Kunde inte ansluta till SMTP-server.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP fel: Data accepterades inte.'; +//$PHPMAILER_LANG['empty_message'] = 'Message body empty'; +$PHPMAILER_LANG['encoding'] = 'Okänt encode-format: '; +$PHPMAILER_LANG['execute'] = 'Kunde inte köra: '; +$PHPMAILER_LANG['file_access'] = 'Ingen åtkomst till fil: '; +$PHPMAILER_LANG['file_open'] = 'Fil fel: Kunde inte öppna fil: '; +$PHPMAILER_LANG['from_failed'] = 'Följande avsändaradress är felaktig: '; +$PHPMAILER_LANG['instantiate'] = 'Kunde inte initiera e-postfunktion.'; +$PHPMAILER_LANG['invalid_address'] = 'Felaktig adress: '; +$PHPMAILER_LANG['provide_address'] = 'Du måste ange minst en mottagares e-postadress.'; +$PHPMAILER_LANG['mailer_not_supported'] = ' mailer stöds inte.'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP fel: Följande mottagare är felaktig: '; +$PHPMAILER_LANG['signing'] = 'Signerings fel: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP Connect() misslyckades.'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP server fel: '; +$PHPMAILER_LANG['variable_set'] = 'Kunde inte definiera eller återställa variabel: '; +$PHPMAILER_LANG['extension_missing'] = 'Tillägg ej tillgängligt: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-tl.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-tl.php new file mode 100644 index 0000000000..ed51d4c601 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-tl.php @@ -0,0 +1,27 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP Error: Hindi mapatotohanan.'; +$PHPMAILER_LANG['connect_host'] = 'SMTP Error: Hindi makakonekta sa SMTP host.'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP Error: Ang datos ay hindi maaaring matatanggap.'; +$PHPMAILER_LANG['empty_message'] = 'Walang laman ang mensahe'; +$PHPMAILER_LANG['encoding'] = 'Hindi alam ang encoding: '; +$PHPMAILER_LANG['execute'] = 'Hindi maisasagawa: '; +$PHPMAILER_LANG['file_access'] = 'Hindi ma-access ang file: '; +$PHPMAILER_LANG['file_open'] = 'Hindi mabuksan ang file: '; +$PHPMAILER_LANG['from_failed'] = 'Ang sumusunod na address ay nabigo: '; +$PHPMAILER_LANG['instantiate'] = 'Hindi maaaring magbigay ng institusyon ang mail'; +$PHPMAILER_LANG['invalid_address'] = 'Hindi wasto ang address na naibigay: '; +$PHPMAILER_LANG['mailer_not_supported'] = 'Ang mailer ay hindi suportado'; +$PHPMAILER_LANG['provide_address'] = 'Kailangan mong magbigay ng kahit isang email address na tatanggap'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP Error: Ang mga sumusunod na tatanggap ay nabigo: '; +$PHPMAILER_LANG['signing'] = 'Hindi ma-sign'; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Ang SMTP connect() ay nabigo'; +$PHPMAILER_LANG['smtp_error'] = 'Ang server ng SMTP ay nabigo'; +$PHPMAILER_LANG['variable_set'] = 'Hindi matatakda ang mga variables: '; +$PHPMAILER_LANG['extension_missing'] = 'Nawawala ang extension'; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-tr.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-tr.php new file mode 100644 index 0000000000..cfe8eaae24 --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-tr.php @@ -0,0 +1,30 @@ + + * @fixed by Boris Yurchenko + */ + +$PHPMAILER_LANG['authenticate'] = 'Помилка SMTP: помилка авторизації.'; +$PHPMAILER_LANG['connect_host'] = 'Помилка SMTP: не вдається під\'єднатися до SMTP-серверу.'; +$PHPMAILER_LANG['data_not_accepted'] = 'Помилка SMTP: дані не прийнято.'; +$PHPMAILER_LANG['encoding'] = 'Невідоме кодування: '; +$PHPMAILER_LANG['execute'] = 'Неможливо виконати команду: '; +$PHPMAILER_LANG['file_access'] = 'Немає доступу до файлу: '; +$PHPMAILER_LANG['file_open'] = 'Помилка файлової системи: не вдається відкрити файл: '; +$PHPMAILER_LANG['from_failed'] = 'Невірна адреса відправника: '; +$PHPMAILER_LANG['instantiate'] = 'Неможливо запустити функцію mail().'; +$PHPMAILER_LANG['provide_address'] = 'Будь-ласка, введіть хоча б одну email-адресу отримувача.'; +$PHPMAILER_LANG['mailer_not_supported'] = ' - поштовий сервер не підтримується.'; +$PHPMAILER_LANG['recipients_failed'] = 'Помилка SMTP: не вдалося відправлення для таких отримувачів: '; +$PHPMAILER_LANG['empty_message'] = 'Пусте повідомлення'; +$PHPMAILER_LANG['invalid_address'] = 'Не відправлено через невірний формат email-адреси: '; +$PHPMAILER_LANG['signing'] = 'Помилка підпису: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Помилка з\'єднання з SMTP-сервером'; +$PHPMAILER_LANG['smtp_error'] = 'Помилка SMTP-сервера: '; +$PHPMAILER_LANG['variable_set'] = 'Неможливо встановити або скинути змінну: '; +$PHPMAILER_LANG['extension_missing'] = 'Розширення відсутнє: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-vi.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-vi.php new file mode 100644 index 0000000000..c60dadebdb --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-vi.php @@ -0,0 +1,26 @@ + + */ + +$PHPMAILER_LANG['authenticate'] = 'Lỗi SMTP: Không thể xác thực.'; +$PHPMAILER_LANG['connect_host'] = 'Lỗi SMTP: Không thể kết nối máy chủ SMTP.'; +$PHPMAILER_LANG['data_not_accepted'] = 'Lỗi SMTP: Dữ liệu không được chấp nhận.'; +$PHPMAILER_LANG['empty_message'] = 'Không có nội dung'; +$PHPMAILER_LANG['encoding'] = 'Mã hóa không xác định: '; +$PHPMAILER_LANG['execute'] = 'Không thực hiện được: '; +$PHPMAILER_LANG['file_access'] = 'Không thể truy cập tệp tin '; +$PHPMAILER_LANG['file_open'] = 'Lỗi Tập tin: Không thể mở tệp tin: '; +$PHPMAILER_LANG['from_failed'] = 'Lỗi địa chỉ gửi đi: '; +$PHPMAILER_LANG['instantiate'] = 'Không dùng được các hàm gửi thư.'; +$PHPMAILER_LANG['invalid_address'] = 'Đại chỉ emai không đúng: '; +$PHPMAILER_LANG['mailer_not_supported'] = ' trình gửi thư không được hỗ trợ.'; +$PHPMAILER_LANG['provide_address'] = 'Bạn phải cung cấp ít nhất một địa chỉ người nhận.'; +$PHPMAILER_LANG['recipients_failed'] = 'Lỗi SMTP: lỗi địa chỉ người nhận: '; +$PHPMAILER_LANG['signing'] = 'Lỗi đăng nhập: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'Lỗi kết nối với SMTP'; +$PHPMAILER_LANG['smtp_error'] = 'Lỗi máy chủ smtp '; +$PHPMAILER_LANG['variable_set'] = 'Không thể thiết lập hoặc thiết lập lại biến: '; +//$PHPMAILER_LANG['extension_missing'] = 'Extension missing: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-zh.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-zh.php new file mode 100644 index 0000000000..3e9e358ceb --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-zh.php @@ -0,0 +1,28 @@ + + * @author Peter Dave Hello <@PeterDaveHello/> + * @author Jason Chiang + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP 錯誤:登入失敗。'; +$PHPMAILER_LANG['connect_host'] = 'SMTP 錯誤:無法連線到 SMTP 主機。'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP 錯誤:無法接受的資料。'; +$PHPMAILER_LANG['empty_message'] = '郵件內容為空'; +$PHPMAILER_LANG['encoding'] = '未知編碼: '; +$PHPMAILER_LANG['execute'] = '無法執行:'; +$PHPMAILER_LANG['file_access'] = '無法存取檔案:'; +$PHPMAILER_LANG['file_open'] = '檔案錯誤:無法開啟檔案:'; +$PHPMAILER_LANG['from_failed'] = '發送地址錯誤:'; +$PHPMAILER_LANG['instantiate'] = '未知函數呼叫。'; +$PHPMAILER_LANG['invalid_address'] = '因為電子郵件地址無效,無法傳送: '; +$PHPMAILER_LANG['mailer_not_supported'] = '不支援的發信客戶端。'; +$PHPMAILER_LANG['provide_address'] = '必須提供至少一個收件人地址。'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP 錯誤:以下收件人地址錯誤:'; +$PHPMAILER_LANG['signing'] = '電子簽章錯誤: '; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP 連線失敗'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP 伺服器錯誤: '; +$PHPMAILER_LANG['variable_set'] = '無法設定或重設變數: '; +$PHPMAILER_LANG['extension_missing'] = '遺失模組 Extension: '; diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-zh_cn.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-zh_cn.php new file mode 100644 index 0000000000..37537802aa --- /dev/null +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-zh_cn.php @@ -0,0 +1,28 @@ + + * @author young + * @author Teddysun + */ + +$PHPMAILER_LANG['authenticate'] = 'SMTP 错误:登录失败。'; +$PHPMAILER_LANG['connect_host'] = 'SMTP 错误:无法连接到 SMTP 主机。'; +$PHPMAILER_LANG['data_not_accepted'] = 'SMTP 错误:数据不被接受。'; +$PHPMAILER_LANG['empty_message'] = '邮件正文为空。'; +$PHPMAILER_LANG['encoding'] = '未知编码:'; +$PHPMAILER_LANG['execute'] = '无法执行:'; +$PHPMAILER_LANG['file_access'] = '无法访问文件:'; +$PHPMAILER_LANG['file_open'] = '文件错误:无法打开文件:'; +$PHPMAILER_LANG['from_failed'] = '发送地址错误:'; +$PHPMAILER_LANG['instantiate'] = '未知函数调用。'; +$PHPMAILER_LANG['invalid_address'] = '发送失败,电子邮箱地址是无效的:'; +$PHPMAILER_LANG['mailer_not_supported'] = '发信客户端不被支持。'; +$PHPMAILER_LANG['provide_address'] = '必须提供至少一个收件人地址。'; +$PHPMAILER_LANG['recipients_failed'] = 'SMTP 错误:收件人地址错误:'; +$PHPMAILER_LANG['signing'] = '登录失败:'; +$PHPMAILER_LANG['smtp_connect_failed'] = 'SMTP服务器连接失败。'; +$PHPMAILER_LANG['smtp_error'] = 'SMTP服务器出错:'; +$PHPMAILER_LANG['variable_set'] = '无法设置或重置变量:'; +$PHPMAILER_LANG['extension_missing'] = '丢失模块 Extension:'; diff --git a/vendor/phpmailer/phpmailer/src/Exception.php b/vendor/phpmailer/phpmailer/src/Exception.php new file mode 100644 index 0000000000..b1e552f50b --- /dev/null +++ b/vendor/phpmailer/phpmailer/src/Exception.php @@ -0,0 +1,39 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2017 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +namespace PHPMailer\PHPMailer; + +/** + * PHPMailer exception handler. + * + * @author Marcus Bointon + */ +class Exception extends \Exception +{ + /** + * Prettify error message output. + * + * @return string + */ + public function errorMessage() + { + return '' . htmlspecialchars($this->getMessage()) . "
\n"; + } +} diff --git a/vendor/phpmailer/phpmailer/src/OAuth.php b/vendor/phpmailer/phpmailer/src/OAuth.php new file mode 100644 index 0000000000..0bce7e3468 --- /dev/null +++ b/vendor/phpmailer/phpmailer/src/OAuth.php @@ -0,0 +1,138 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2015 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +namespace PHPMailer\PHPMailer; + +use League\OAuth2\Client\Grant\RefreshToken; +use League\OAuth2\Client\Provider\AbstractProvider; +use League\OAuth2\Client\Token\AccessToken; + +/** + * OAuth - OAuth2 authentication wrapper class. + * Uses the oauth2-client package from the League of Extraordinary Packages. + * + * @see http://oauth2-client.thephpleague.com + * + * @author Marcus Bointon (Synchro/coolbru) + */ +class OAuth +{ + /** + * An instance of the League OAuth Client Provider. + * + * @var AbstractProvider + */ + protected $provider; + + /** + * The current OAuth access token. + * + * @var AccessToken + */ + protected $oauthToken; + + /** + * The user's email address, usually used as the login ID + * and also the from address when sending email. + * + * @var string + */ + protected $oauthUserEmail = ''; + + /** + * The client secret, generated in the app definition of the service you're connecting to. + * + * @var string + */ + protected $oauthClientSecret = ''; + + /** + * The client ID, generated in the app definition of the service you're connecting to. + * + * @var string + */ + protected $oauthClientId = ''; + + /** + * The refresh token, used to obtain new AccessTokens. + * + * @var string + */ + protected $oauthRefreshToken = ''; + + /** + * OAuth constructor. + * + * @param array $options Associative array containing + * `provider`, `userName`, `clientSecret`, `clientId` and `refreshToken` elements + */ + public function __construct($options) + { + $this->provider = $options['provider']; + $this->oauthUserEmail = $options['userName']; + $this->oauthClientSecret = $options['clientSecret']; + $this->oauthClientId = $options['clientId']; + $this->oauthRefreshToken = $options['refreshToken']; + } + + /** + * Get a new RefreshToken. + * + * @return RefreshToken + */ + protected function getGrant() + { + return new RefreshToken(); + } + + /** + * Get a new AccessToken. + * + * @return AccessToken + */ + protected function getToken() + { + return $this->provider->getAccessToken( + $this->getGrant(), + ['refresh_token' => $this->oauthRefreshToken] + ); + } + + /** + * Generate a base64-encoded OAuth token. + * + * @return string + */ + public function getOauth64() + { + // Get a new token if it's not available or has expired + if (null === $this->oauthToken or $this->oauthToken->hasExpired()) { + $this->oauthToken = $this->getToken(); + } + + return base64_encode( + 'user=' . + $this->oauthUserEmail . + "\001auth=Bearer " . + $this->oauthToken . + "\001\001" + ); + } +} diff --git a/vendor/phpmailer/phpmailer/src/PHPMailer.php b/vendor/phpmailer/phpmailer/src/PHPMailer.php new file mode 100644 index 0000000000..4897b5e231 --- /dev/null +++ b/vendor/phpmailer/phpmailer/src/PHPMailer.php @@ -0,0 +1,4697 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2019 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +namespace PHPMailer\PHPMailer; + +/** + * PHPMailer - PHP email creation and transport class. + * + * @author Marcus Bointon (Synchro/coolbru) + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + */ +class PHPMailer +{ + const CHARSET_ASCII = 'us-ascii'; + const CHARSET_ISO88591 = 'iso-8859-1'; + const CHARSET_UTF8 = 'utf-8'; + + const CONTENT_TYPE_PLAINTEXT = 'text/plain'; + const CONTENT_TYPE_TEXT_CALENDAR = 'text/calendar'; + const CONTENT_TYPE_TEXT_HTML = 'text/html'; + const CONTENT_TYPE_MULTIPART_ALTERNATIVE = 'multipart/alternative'; + const CONTENT_TYPE_MULTIPART_MIXED = 'multipart/mixed'; + const CONTENT_TYPE_MULTIPART_RELATED = 'multipart/related'; + + const ENCODING_7BIT = '7bit'; + const ENCODING_8BIT = '8bit'; + const ENCODING_BASE64 = 'base64'; + const ENCODING_BINARY = 'binary'; + const ENCODING_QUOTED_PRINTABLE = 'quoted-printable'; + + const ENCRYPTION_STARTTLS = 'tls'; + const ENCRYPTION_SMTPS = 'ssl'; + + const ICAL_METHOD_REQUEST = 'REQUEST'; + const ICAL_METHOD_PUBLISH = 'PUBLISH'; + const ICAL_METHOD_REPLY = 'REPLY'; + const ICAL_METHOD_ADD = 'ADD'; + const ICAL_METHOD_CANCEL = 'CANCEL'; + const ICAL_METHOD_REFRESH = 'REFRESH'; + const ICAL_METHOD_COUNTER = 'COUNTER'; + const ICAL_METHOD_DECLINECOUNTER = 'DECLINECOUNTER'; + + /** + * Email priority. + * Options: null (default), 1 = High, 3 = Normal, 5 = low. + * When null, the header is not set at all. + * + * @var int + */ + public $Priority; + + /** + * The character set of the message. + * + * @var string + */ + public $CharSet = self::CHARSET_ISO88591; + + /** + * The MIME Content-type of the message. + * + * @var string + */ + public $ContentType = self::CONTENT_TYPE_PLAINTEXT; + + /** + * The message encoding. + * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable". + * + * @var string + */ + public $Encoding = self::ENCODING_8BIT; + + /** + * Holds the most recent mailer error message. + * + * @var string + */ + public $ErrorInfo = ''; + + /** + * The From email address for the message. + * + * @var string + */ + public $From = 'root@localhost'; + + /** + * The From name of the message. + * + * @var string + */ + public $FromName = 'Root User'; + + /** + * The envelope sender of the message. + * This will usually be turned into a Return-Path header by the receiver, + * and is the address that bounces will be sent to. + * If not empty, will be passed via `-f` to sendmail or as the 'MAIL FROM' value over SMTP. + * + * @var string + */ + public $Sender = ''; + + /** + * The Subject of the message. + * + * @var string + */ + public $Subject = ''; + + /** + * An HTML or plain text message body. + * If HTML then call isHTML(true). + * + * @var string + */ + public $Body = ''; + + /** + * The plain-text message body. + * This body can be read by mail clients that do not have HTML email + * capability such as mutt & Eudora. + * Clients that can read HTML will view the normal Body. + * + * @var string + */ + public $AltBody = ''; + + /** + * An iCal message part body. + * Only supported in simple alt or alt_inline message types + * To generate iCal event structures, use classes like EasyPeasyICS or iCalcreator. + * + * @see http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/ + * @see http://kigkonsult.se/iCalcreator/ + * + * @var string + */ + public $Ical = ''; + + /** + * Value-array of "method" in Contenttype header "text/calendar" + * + * @var string[] + */ + protected static $IcalMethods = [ + self::ICAL_METHOD_REQUEST, + self::ICAL_METHOD_PUBLISH, + self::ICAL_METHOD_REPLY, + self::ICAL_METHOD_ADD, + self::ICAL_METHOD_CANCEL, + self::ICAL_METHOD_REFRESH, + self::ICAL_METHOD_COUNTER, + self::ICAL_METHOD_DECLINECOUNTER, + ]; + + /** + * The complete compiled MIME message body. + * + * @var string + */ + protected $MIMEBody = ''; + + /** + * The complete compiled MIME message headers. + * + * @var string + */ + protected $MIMEHeader = ''; + + /** + * Extra headers that createHeader() doesn't fold in. + * + * @var string + */ + protected $mailHeader = ''; + + /** + * Word-wrap the message body to this number of chars. + * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance. + * + * @see static::STD_LINE_LENGTH + * + * @var int + */ + public $WordWrap = 0; + + /** + * Which method to use to send mail. + * Options: "mail", "sendmail", or "smtp". + * + * @var string + */ + public $Mailer = 'mail'; + + /** + * The path to the sendmail program. + * + * @var string + */ + public $Sendmail = '/usr/sbin/sendmail'; + + /** + * Whether mail() uses a fully sendmail-compatible MTA. + * One which supports sendmail's "-oi -f" options. + * + * @var bool + */ + public $UseSendmailOptions = true; + + /** + * The email address that a reading confirmation should be sent to, also known as read receipt. + * + * @var string + */ + public $ConfirmReadingTo = ''; + + /** + * The hostname to use in the Message-ID header and as default HELO string. + * If empty, PHPMailer attempts to find one with, in order, + * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value + * 'localhost.localdomain'. + * + * @see PHPMailer::$Helo + * + * @var string + */ + public $Hostname = ''; + + /** + * An ID to be used in the Message-ID header. + * If empty, a unique id will be generated. + * You can set your own, but it must be in the format "", + * as defined in RFC5322 section 3.6.4 or it will be ignored. + * + * @see https://tools.ietf.org/html/rfc5322#section-3.6.4 + * + * @var string + */ + public $MessageID = ''; + + /** + * The message Date to be used in the Date header. + * If empty, the current date will be added. + * + * @var string + */ + public $MessageDate = ''; + + /** + * SMTP hosts. + * Either a single hostname or multiple semicolon-delimited hostnames. + * You can also specify a different port + * for each host by using this format: [hostname:port] + * (e.g. "smtp1.example.com:25;smtp2.example.com"). + * You can also specify encryption type, for example: + * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465"). + * Hosts will be tried in order. + * + * @var string + */ + public $Host = 'localhost'; + + /** + * The default SMTP server port. + * + * @var int + */ + public $Port = 25; + + /** + * The SMTP HELO/EHLO name used for the SMTP connection. + * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find + * one with the same method described above for $Hostname. + * + * @see PHPMailer::$Hostname + * + * @var string + */ + public $Helo = ''; + + /** + * What kind of encryption to use on the SMTP connection. + * Options: '', static::ENCRYPTION_STARTTLS, or static::ENCRYPTION_SMTPS. + * + * @var string + */ + public $SMTPSecure = ''; + + /** + * Whether to enable TLS encryption automatically if a server supports it, + * even if `SMTPSecure` is not set to 'tls'. + * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid. + * + * @var bool + */ + public $SMTPAutoTLS = true; + + /** + * Whether to use SMTP authentication. + * Uses the Username and Password properties. + * + * @see PHPMailer::$Username + * @see PHPMailer::$Password + * + * @var bool + */ + public $SMTPAuth = false; + + /** + * Options array passed to stream_context_create when connecting via SMTP. + * + * @var array + */ + public $SMTPOptions = []; + + /** + * SMTP username. + * + * @var string + */ + public $Username = ''; + + /** + * SMTP password. + * + * @var string + */ + public $Password = ''; + + /** + * SMTP auth type. + * Options are CRAM-MD5, LOGIN, PLAIN, XOAUTH2, attempted in that order if not specified. + * + * @var string + */ + public $AuthType = ''; + + /** + * An instance of the PHPMailer OAuth class. + * + * @var OAuth + */ + protected $oauth; + + /** + * The SMTP server timeout in seconds. + * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2. + * + * @var int + */ + public $Timeout = 300; + + /** + * Comma separated list of DSN notifications + * 'NEVER' under no circumstances a DSN must be returned to the sender. + * If you use NEVER all other notifications will be ignored. + * 'SUCCESS' will notify you when your mail has arrived at its destination. + * 'FAILURE' will arrive if an error occurred during delivery. + * 'DELAY' will notify you if there is an unusual delay in delivery, but the actual + * delivery's outcome (success or failure) is not yet decided. + * + * @see https://tools.ietf.org/html/rfc3461 See section 4.1 for more information about NOTIFY + */ + public $dsn = ''; + + /** + * SMTP class debug output mode. + * Debug output level. + * Options: + * * SMTP::DEBUG_OFF: No output + * * SMTP::DEBUG_CLIENT: Client messages + * * SMTP::DEBUG_SERVER: Client and server messages + * * SMTP::DEBUG_CONNECTION: As SERVER plus connection status + * * SMTP::DEBUG_LOWLEVEL: Noisy, low-level data output, rarely needed + * + * @see SMTP::$do_debug + * + * @var int + */ + public $SMTPDebug = 0; + + /** + * How to handle debug output. + * Options: + * * `echo` Output plain-text as-is, appropriate for CLI + * * `html` Output escaped, line breaks converted to `
`, appropriate for browser output + * * `error_log` Output to error log as configured in php.ini + * By default PHPMailer will use `echo` if run from a `cli` or `cli-server` SAPI, `html` otherwise. + * Alternatively, you can provide a callable expecting two params: a message string and the debug level: + * + * ```php + * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; + * ``` + * + * Alternatively, you can pass in an instance of a PSR-3 compatible logger, though only `debug` + * level output is used: + * + * ```php + * $mail->Debugoutput = new myPsr3Logger; + * ``` + * + * @see SMTP::$Debugoutput + * + * @var string|callable|\Psr\Log\LoggerInterface + */ + public $Debugoutput = 'echo'; + + /** + * Whether to keep SMTP connection open after each message. + * If this is set to true then to close the connection + * requires an explicit call to smtpClose(). + * + * @var bool + */ + public $SMTPKeepAlive = false; + + /** + * Whether to split multiple to addresses into multiple messages + * or send them all in one message. + * Only supported in `mail` and `sendmail` transports, not in SMTP. + * + * @var bool + */ + public $SingleTo = false; + + /** + * Storage for addresses when SingleTo is enabled. + * + * @var array + */ + protected $SingleToArray = []; + + /** + * Whether to generate VERP addresses on send. + * Only applicable when sending via SMTP. + * + * @see https://en.wikipedia.org/wiki/Variable_envelope_return_path + * @see http://www.postfix.org/VERP_README.html Postfix VERP info + * + * @var bool + */ + public $do_verp = false; + + /** + * Whether to allow sending messages with an empty body. + * + * @var bool + */ + public $AllowEmpty = false; + + /** + * DKIM selector. + * + * @var string + */ + public $DKIM_selector = ''; + + /** + * DKIM Identity. + * Usually the email address used as the source of the email. + * + * @var string + */ + public $DKIM_identity = ''; + + /** + * DKIM passphrase. + * Used if your key is encrypted. + * + * @var string + */ + public $DKIM_passphrase = ''; + + /** + * DKIM signing domain name. + * + * @example 'example.com' + * + * @var string + */ + public $DKIM_domain = ''; + + /** + * DKIM Copy header field values for diagnostic use. + * + * @var bool + */ + public $DKIM_copyHeaderFields = true; + + /** + * DKIM Extra signing headers. + * + * @example ['List-Unsubscribe', 'List-Help'] + * + * @var array + */ + public $DKIM_extraHeaders = []; + + /** + * DKIM private key file path. + * + * @var string + */ + public $DKIM_private = ''; + + /** + * DKIM private key string. + * + * If set, takes precedence over `$DKIM_private`. + * + * @var string + */ + public $DKIM_private_string = ''; + + /** + * Callback Action function name. + * + * The function that handles the result of the send email action. + * It is called out by send() for each email sent. + * + * Value can be any php callable: http://www.php.net/is_callable + * + * Parameters: + * bool $result result of the send action + * array $to email addresses of the recipients + * array $cc cc email addresses + * array $bcc bcc email addresses + * string $subject the subject + * string $body the email body + * string $from email address of sender + * string $extra extra information of possible use + * "smtp_transaction_id' => last smtp transaction id + * + * @var string + */ + public $action_function = ''; + + /** + * What to put in the X-Mailer header. + * Options: An empty string for PHPMailer default, whitespace/null for none, or a string to use. + * + * @var string|null + */ + public $XMailer = ''; + + /** + * Which validator to use by default when validating email addresses. + * May be a callable to inject your own validator, but there are several built-in validators. + * The default validator uses PHP's FILTER_VALIDATE_EMAIL filter_var option. + * + * @see PHPMailer::validateAddress() + * + * @var string|callable + */ + public static $validator = 'php'; + + /** + * An instance of the SMTP sender class. + * + * @var SMTP + */ + protected $smtp; + + /** + * The array of 'to' names and addresses. + * + * @var array + */ + protected $to = []; + + /** + * The array of 'cc' names and addresses. + * + * @var array + */ + protected $cc = []; + + /** + * The array of 'bcc' names and addresses. + * + * @var array + */ + protected $bcc = []; + + /** + * The array of reply-to names and addresses. + * + * @var array + */ + protected $ReplyTo = []; + + /** + * An array of all kinds of addresses. + * Includes all of $to, $cc, $bcc. + * + * @see PHPMailer::$to + * @see PHPMailer::$cc + * @see PHPMailer::$bcc + * + * @var array + */ + protected $all_recipients = []; + + /** + * An array of names and addresses queued for validation. + * In send(), valid and non duplicate entries are moved to $all_recipients + * and one of $to, $cc, or $bcc. + * This array is used only for addresses with IDN. + * + * @see PHPMailer::$to + * @see PHPMailer::$cc + * @see PHPMailer::$bcc + * @see PHPMailer::$all_recipients + * + * @var array + */ + protected $RecipientsQueue = []; + + /** + * An array of reply-to names and addresses queued for validation. + * In send(), valid and non duplicate entries are moved to $ReplyTo. + * This array is used only for addresses with IDN. + * + * @see PHPMailer::$ReplyTo + * + * @var array + */ + protected $ReplyToQueue = []; + + /** + * The array of attachments. + * + * @var array + */ + protected $attachment = []; + + /** + * The array of custom headers. + * + * @var array + */ + protected $CustomHeader = []; + + /** + * The most recent Message-ID (including angular brackets). + * + * @var string + */ + protected $lastMessageID = ''; + + /** + * The message's MIME type. + * + * @var string + */ + protected $message_type = ''; + + /** + * The array of MIME boundary strings. + * + * @var array + */ + protected $boundary = []; + + /** + * The array of available languages. + * + * @var array + */ + protected $language = []; + + /** + * The number of errors encountered. + * + * @var int + */ + protected $error_count = 0; + + /** + * The S/MIME certificate file path. + * + * @var string + */ + protected $sign_cert_file = ''; + + /** + * The S/MIME key file path. + * + * @var string + */ + protected $sign_key_file = ''; + + /** + * The optional S/MIME extra certificates ("CA Chain") file path. + * + * @var string + */ + protected $sign_extracerts_file = ''; + + /** + * The S/MIME password for the key. + * Used only if the key is encrypted. + * + * @var string + */ + protected $sign_key_pass = ''; + + /** + * Whether to throw exceptions for errors. + * + * @var bool + */ + protected $exceptions = false; + + /** + * Unique ID used for message ID and boundaries. + * + * @var string + */ + protected $uniqueid = ''; + + /** + * The PHPMailer Version number. + * + * @var string + */ + const VERSION = '6.1.1'; + + /** + * Error severity: message only, continue processing. + * + * @var int + */ + const STOP_MESSAGE = 0; + + /** + * Error severity: message, likely ok to continue processing. + * + * @var int + */ + const STOP_CONTINUE = 1; + + /** + * Error severity: message, plus full stop, critical error reached. + * + * @var int + */ + const STOP_CRITICAL = 2; + + /** + * SMTP RFC standard line ending. + * + * @var string + */ + protected static $LE = "\r\n"; + + /** + * The maximum line length supported by mail(). + * + * Background: mail() will sometimes corrupt messages + * with headers headers longer than 65 chars, see #818. + * + * @var int + */ + const MAIL_MAX_LINE_LENGTH = 63; + + /** + * The maximum line length allowed by RFC 2822 section 2.1.1. + * + * @var int + */ + const MAX_LINE_LENGTH = 998; + + /** + * The lower maximum line length allowed by RFC 2822 section 2.1.1. + * This length does NOT include the line break + * 76 means that lines will be 77 or 78 chars depending on whether + * the line break format is LF or CRLF; both are valid. + * + * @var int + */ + const STD_LINE_LENGTH = 76; + + /** + * Constructor. + * + * @param bool $exceptions Should we throw external exceptions? + */ + public function __construct($exceptions = null) + { + if (null !== $exceptions) { + $this->exceptions = (bool) $exceptions; + } + //Pick an appropriate debug output format automatically + $this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html'); + } + + /** + * Destructor. + */ + public function __destruct() + { + //Close any open SMTP connection nicely + $this->smtpClose(); + } + + /** + * Call mail() in a safe_mode-aware fashion. + * Also, unless sendmail_path points to sendmail (or something that + * claims to be sendmail), don't pass params (not a perfect fix, + * but it will do). + * + * @param string $to To + * @param string $subject Subject + * @param string $body Message Body + * @param string $header Additional Header(s) + * @param string|null $params Params + * + * @return bool + */ + private function mailPassthru($to, $subject, $body, $header, $params) + { + //Check overloading of mail function to avoid double-encoding + if (ini_get('mbstring.func_overload') & 1) { + $subject = $this->secureHeader($subject); + } else { + $subject = $this->encodeHeader($this->secureHeader($subject)); + } + //Calling mail() with null params breaks + if (!$this->UseSendmailOptions or null === $params) { + $result = @mail($to, $subject, $body, $header); + } else { + $result = @mail($to, $subject, $body, $header, $params); + } + + return $result; + } + + /** + * Output debugging info via user-defined method. + * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug). + * + * @see PHPMailer::$Debugoutput + * @see PHPMailer::$SMTPDebug + * + * @param string $str + */ + protected function edebug($str) + { + if ($this->SMTPDebug <= 0) { + return; + } + //Is this a PSR-3 logger? + if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) { + $this->Debugoutput->debug($str); + + return; + } + //Avoid clash with built-in function names + if (!in_array($this->Debugoutput, ['error_log', 'html', 'echo']) and is_callable($this->Debugoutput)) { + call_user_func($this->Debugoutput, $str, $this->SMTPDebug); + + return; + } + switch ($this->Debugoutput) { + case 'error_log': + //Don't output, just log + error_log($str); + break; + case 'html': + //Cleans up output a bit for a better looking, HTML-safe output + echo htmlentities( + preg_replace('/[\r\n]+/', '', $str), + ENT_QUOTES, + 'UTF-8' + ), "
\n"; + break; + case 'echo': + default: + //Normalize line breaks + $str = preg_replace('/\r\n|\r/ms', "\n", $str); + echo gmdate('Y-m-d H:i:s'), + "\t", + //Trim trailing space + trim( + //Indent for readability, except for trailing break + str_replace( + "\n", + "\n \t ", + trim($str) + ) + ), + "\n"; + } + } + + /** + * Sets message type to HTML or plain. + * + * @param bool $isHtml True for HTML mode + */ + public function isHTML($isHtml = true) + { + if ($isHtml) { + $this->ContentType = static::CONTENT_TYPE_TEXT_HTML; + } else { + $this->ContentType = static::CONTENT_TYPE_PLAINTEXT; + } + } + + /** + * Send messages using SMTP. + */ + public function isSMTP() + { + $this->Mailer = 'smtp'; + } + + /** + * Send messages using PHP's mail() function. + */ + public function isMail() + { + $this->Mailer = 'mail'; + } + + /** + * Send messages using $Sendmail. + */ + public function isSendmail() + { + $ini_sendmail_path = ini_get('sendmail_path'); + + if (false === stripos($ini_sendmail_path, 'sendmail')) { + $this->Sendmail = '/usr/sbin/sendmail'; + } else { + $this->Sendmail = $ini_sendmail_path; + } + $this->Mailer = 'sendmail'; + } + + /** + * Send messages using qmail. + */ + public function isQmail() + { + $ini_sendmail_path = ini_get('sendmail_path'); + + if (false === stripos($ini_sendmail_path, 'qmail')) { + $this->Sendmail = '/var/qmail/bin/qmail-inject'; + } else { + $this->Sendmail = $ini_sendmail_path; + } + $this->Mailer = 'qmail'; + } + + /** + * Add a "To" address. + * + * @param string $address The email address to send to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + public function addAddress($address, $name = '') + { + return $this->addOrEnqueueAnAddress('to', $address, $name); + } + + /** + * Add a "CC" address. + * + * @param string $address The email address to send to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + public function addCC($address, $name = '') + { + return $this->addOrEnqueueAnAddress('cc', $address, $name); + } + + /** + * Add a "BCC" address. + * + * @param string $address The email address to send to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + public function addBCC($address, $name = '') + { + return $this->addOrEnqueueAnAddress('bcc', $address, $name); + } + + /** + * Add a "Reply-To" address. + * + * @param string $address The email address to reply to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + public function addReplyTo($address, $name = '') + { + return $this->addOrEnqueueAnAddress('Reply-To', $address, $name); + } + + /** + * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer + * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still + * be modified after calling this function), addition of such addresses is delayed until send(). + * Addresses that have been added already return false, but do not throw exceptions. + * + * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' + * @param string $address The email address to send, resp. to reply to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + protected function addOrEnqueueAnAddress($kind, $address, $name) + { + $address = trim($address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + $pos = strrpos($address, '@'); + if (false === $pos) { + // At-sign is missing. + $error_message = sprintf( + '%s (%s): %s', + $this->lang('invalid_address'), + $kind, + $address + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + $params = [$kind, $address, $name]; + // Enqueue addresses with IDN until we know the PHPMailer::$CharSet. + if ($this->has8bitChars(substr($address, ++$pos)) and static::idnSupported()) { + if ('Reply-To' != $kind) { + if (!array_key_exists($address, $this->RecipientsQueue)) { + $this->RecipientsQueue[$address] = $params; + + return true; + } + } else { + if (!array_key_exists($address, $this->ReplyToQueue)) { + $this->ReplyToQueue[$address] = $params; + + return true; + } + } + + return false; + } + + // Immediately add standard addresses without IDN. + return call_user_func_array([$this, 'addAnAddress'], $params); + } + + /** + * Add an address to one of the recipient arrays or to the ReplyTo array. + * Addresses that have been added already return false, but do not throw exceptions. + * + * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' + * @param string $address The email address to send, resp. to reply to + * @param string $name + * + * @throws Exception + * + * @return bool true on success, false if address already used or invalid in some way + */ + protected function addAnAddress($kind, $address, $name = '') + { + if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) { + $error_message = sprintf( + '%s: %s', + $this->lang('Invalid recipient kind'), + $kind + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + if (!static::validateAddress($address)) { + $error_message = sprintf( + '%s (%s): %s', + $this->lang('invalid_address'), + $kind, + $address + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + if ('Reply-To' != $kind) { + if (!array_key_exists(strtolower($address), $this->all_recipients)) { + $this->{$kind}[] = [$address, $name]; + $this->all_recipients[strtolower($address)] = true; + + return true; + } + } else { + if (!array_key_exists(strtolower($address), $this->ReplyTo)) { + $this->ReplyTo[strtolower($address)] = [$address, $name]; + + return true; + } + } + + return false; + } + + /** + * Parse and validate a string containing one or more RFC822-style comma-separated email addresses + * of the form "display name
" into an array of name/address pairs. + * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available. + * Note that quotes in the name part are removed. + * + * @see http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation + * + * @param string $addrstr The address list string + * @param bool $useimap Whether to use the IMAP extension to parse the list + * + * @return array + */ + public static function parseAddresses($addrstr, $useimap = true) + { + $addresses = []; + if ($useimap and function_exists('imap_rfc822_parse_adrlist')) { + //Use this built-in parser if it's available + $list = imap_rfc822_parse_adrlist($addrstr, ''); + foreach ($list as $address) { + if ('.SYNTAX-ERROR.' != $address->host) { + if (static::validateAddress($address->mailbox . '@' . $address->host)) { + $addresses[] = [ + 'name' => (property_exists($address, 'personal') ? $address->personal : ''), + 'address' => $address->mailbox . '@' . $address->host, + ]; + } + } + } + } else { + //Use this simpler parser + $list = explode(',', $addrstr); + foreach ($list as $address) { + $address = trim($address); + //Is there a separate name part? + if (strpos($address, '<') === false) { + //No separate name, just use the whole thing + if (static::validateAddress($address)) { + $addresses[] = [ + 'name' => '', + 'address' => $address, + ]; + } + } else { + list($name, $email) = explode('<', $address); + $email = trim(str_replace('>', '', $email)); + if (static::validateAddress($email)) { + $addresses[] = [ + 'name' => trim(str_replace(['"', "'"], '', $name)), + 'address' => $email, + ]; + } + } + } + } + + return $addresses; + } + + /** + * Set the From and FromName properties. + * + * @param string $address + * @param string $name + * @param bool $auto Whether to also set the Sender address, defaults to true + * + * @throws Exception + * + * @return bool + */ + public function setFrom($address, $name = '', $auto = true) + { + $address = trim($address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + // Don't validate now addresses with IDN. Will be done in send(). + $pos = strrpos($address, '@'); + if (false === $pos + or (!$this->has8bitChars(substr($address, ++$pos)) or !static::idnSupported()) + and !static::validateAddress($address) + ) { + $error_message = sprintf( + '%s (From): %s', + $this->lang('invalid_address'), + $address + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + $this->From = $address; + $this->FromName = $name; + if ($auto) { + if (empty($this->Sender)) { + $this->Sender = $address; + } + } + + return true; + } + + /** + * Return the Message-ID header of the last email. + * Technically this is the value from the last time the headers were created, + * but it's also the message ID of the last sent message except in + * pathological cases. + * + * @return string + */ + public function getLastMessageID() + { + return $this->lastMessageID; + } + + /** + * Check that a string looks like an email address. + * Validation patterns supported: + * * `auto` Pick best pattern automatically; + * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0; + * * `pcre` Use old PCRE implementation; + * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; + * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. + * * `noregex` Don't use a regex: super fast, really dumb. + * Alternatively you may pass in a callable to inject your own validator, for example: + * + * ```php + * PHPMailer::validateAddress('user@example.com', function($address) { + * return (strpos($address, '@') !== false); + * }); + * ``` + * + * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator. + * + * @param string $address The email address to check + * @param string|callable $patternselect Which pattern to use + * + * @return bool + */ + public static function validateAddress($address, $patternselect = null) + { + if (null === $patternselect) { + $patternselect = static::$validator; + } + if (is_callable($patternselect)) { + return call_user_func($patternselect, $address); + } + //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 + if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) { + return false; + } + switch ($patternselect) { + case 'pcre': //Kept for BC + case 'pcre8': + /* + * A more complex and more permissive version of the RFC5322 regex on which FILTER_VALIDATE_EMAIL + * is based. + * In addition to the addresses allowed by filter_var, also permits: + * * dotless domains: `a@b` + * * comments: `1234 @ local(blah) .machine .example` + * * quoted elements: `'"test blah"@example.org'` + * * numeric TLDs: `a@b.123` + * * unbracketed IPv4 literals: `a@192.168.0.1` + * * IPv6 literals: 'first.last@[IPv6:a1::]' + * Not all of these will necessarily work for sending! + * + * @see http://squiloople.com/2009/12/20/email-address-validation/ + * @copyright 2009-2010 Michael Rushton + * Feel free to use and redistribute this code. But please keep this copyright notice. + */ + return (bool) preg_match( + '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . + '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . + '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . + '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' . + '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' . + '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' . + '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' . + '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' . + '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', + $address + ); + case 'html5': + /* + * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements. + * + * @see http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email) + */ + return (bool) preg_match( + '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . + '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', + $address + ); + case 'php': + default: + return (bool) filter_var($address, FILTER_VALIDATE_EMAIL); + } + } + + /** + * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the + * `intl` and `mbstring` PHP extensions. + * + * @return bool `true` if required functions for IDN support are present + */ + public static function idnSupported() + { + return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding'); + } + + /** + * Converts IDN in given email address to its ASCII form, also known as punycode, if possible. + * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet. + * This function silently returns unmodified address if: + * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form) + * - Conversion to punycode is impossible (e.g. required PHP functions are not available) + * or fails for any reason (e.g. domain contains characters not allowed in an IDN). + * + * @see PHPMailer::$CharSet + * + * @param string $address The email address to convert + * + * @return string The encoded address in ASCII form + */ + public function punyencodeAddress($address) + { + // Verify we have required functions, CharSet, and at-sign. + $pos = strrpos($address, '@'); + if (static::idnSupported() and + !empty($this->CharSet) and + false !== $pos + ) { + $domain = substr($address, ++$pos); + // Verify CharSet string is a valid one, and domain properly encoded in this CharSet. + if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) { + $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet); + //Ignore IDE complaints about this line - method signature changed in PHP 5.4 + $errorcode = 0; + $punycode = idn_to_ascii($domain, $errorcode, INTL_IDNA_VARIANT_UTS46); + if (false !== $punycode) { + return substr($address, 0, $pos) . $punycode; + } + } + } + + return $address; + } + + /** + * Create a message and send it. + * Uses the sending method specified by $Mailer. + * + * @throws Exception + * + * @return bool false on error - See the ErrorInfo property for details of the error + */ + public function send() + { + try { + if (!$this->preSend()) { + return false; + } + + return $this->postSend(); + } catch (Exception $exc) { + $this->mailHeader = ''; + $this->setError($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return false; + } + } + + /** + * Prepare a message for sending. + * + * @throws Exception + * + * @return bool + */ + public function preSend() + { + if ('smtp' == $this->Mailer + or ('mail' == $this->Mailer and stripos(PHP_OS, 'WIN') === 0) + ) { + //SMTP mandates RFC-compliant line endings + //and it's also used with mail() on Windows + static::setLE("\r\n"); + } else { + //Maintain backward compatibility with legacy Linux command line mailers + static::setLE(PHP_EOL); + } + //Check for buggy PHP versions that add a header with an incorrect line break + if (ini_get('mail.add_x_header') == 1 + and 'mail' == $this->Mailer + and stripos(PHP_OS, 'WIN') === 0 + and ((version_compare(PHP_VERSION, '7.0.0', '>=') + and version_compare(PHP_VERSION, '7.0.17', '<')) + or (version_compare(PHP_VERSION, '7.1.0', '>=') + and version_compare(PHP_VERSION, '7.1.3', '<'))) + ) { + trigger_error( + 'Your version of PHP is affected by a bug that may result in corrupted messages.' . + ' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' . + ' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.', + E_USER_WARNING + ); + } + + try { + $this->error_count = 0; // Reset errors + $this->mailHeader = ''; + + // Dequeue recipient and Reply-To addresses with IDN + foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) { + $params[1] = $this->punyencodeAddress($params[1]); + call_user_func_array([$this, 'addAnAddress'], $params); + } + if (count($this->to) + count($this->cc) + count($this->bcc) < 1) { + throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL); + } + + // Validate From, Sender, and ConfirmReadingTo addresses + foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) { + $this->$address_kind = trim($this->$address_kind); + if (empty($this->$address_kind)) { + continue; + } + $this->$address_kind = $this->punyencodeAddress($this->$address_kind); + if (!static::validateAddress($this->$address_kind)) { + $error_message = sprintf( + '%s (%s): %s', + $this->lang('invalid_address'), + $address_kind, + $this->$address_kind + ); + $this->setError($error_message); + $this->edebug($error_message); + if ($this->exceptions) { + throw new Exception($error_message); + } + + return false; + } + } + + // Set whether the message is multipart/alternative + if ($this->alternativeExists()) { + $this->ContentType = static::CONTENT_TYPE_MULTIPART_ALTERNATIVE; + } + + $this->setMessageType(); + // Refuse to send an empty message unless we are specifically allowing it + if (!$this->AllowEmpty and empty($this->Body)) { + throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL); + } + + //Trim subject consistently + $this->Subject = trim($this->Subject); + // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding) + $this->MIMEHeader = ''; + $this->MIMEBody = $this->createBody(); + // createBody may have added some headers, so retain them + $tempheaders = $this->MIMEHeader; + $this->MIMEHeader = $this->createHeader(); + $this->MIMEHeader .= $tempheaders; + + // To capture the complete message when using mail(), create + // an extra header list which createHeader() doesn't fold in + if ('mail' == $this->Mailer) { + if (count($this->to) > 0) { + $this->mailHeader .= $this->addrAppend('To', $this->to); + } else { + $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;'); + } + $this->mailHeader .= $this->headerLine( + 'Subject', + $this->encodeHeader($this->secureHeader($this->Subject)) + ); + } + + // Sign with DKIM if enabled + if (!empty($this->DKIM_domain) + and !empty($this->DKIM_selector) + and (!empty($this->DKIM_private_string) + or (!empty($this->DKIM_private) + and static::isPermittedPath($this->DKIM_private) + and file_exists($this->DKIM_private) + ) + ) + ) { + $header_dkim = $this->DKIM_Add( + $this->MIMEHeader . $this->mailHeader, + $this->encodeHeader($this->secureHeader($this->Subject)), + $this->MIMEBody + ); + $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . static::$LE . + static::normalizeBreaks($header_dkim) . static::$LE; + } + + return true; + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return false; + } + } + + /** + * Actually send a message via the selected mechanism. + * + * @throws Exception + * + * @return bool + */ + public function postSend() + { + try { + // Choose the mailer and send through it + switch ($this->Mailer) { + case 'sendmail': + case 'qmail': + return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody); + case 'smtp': + return $this->smtpSend($this->MIMEHeader, $this->MIMEBody); + case 'mail': + return $this->mailSend($this->MIMEHeader, $this->MIMEBody); + default: + $sendMethod = $this->Mailer . 'Send'; + if (method_exists($this, $sendMethod)) { + return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody); + } + + return $this->mailSend($this->MIMEHeader, $this->MIMEBody); + } + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + } + + return false; + } + + /** + * Send mail using the $Sendmail program. + * + * @see PHPMailer::$Sendmail + * + * @param string $header The message headers + * @param string $body The message body + * + * @throws Exception + * + * @return bool + */ + protected function sendmailSend($header, $body) + { + // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. + if (!empty($this->Sender) and self::isShellSafe($this->Sender)) { + if ('qmail' == $this->Mailer) { + $sendmailFmt = '%s -f%s'; + } else { + $sendmailFmt = '%s -oi -f%s -t'; + } + } else { + if ('qmail' == $this->Mailer) { + $sendmailFmt = '%s'; + } else { + $sendmailFmt = '%s -oi -t'; + } + } + + $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender); + + if ($this->SingleTo) { + foreach ($this->SingleToArray as $toAddr) { + $mail = @popen($sendmail, 'w'); + if (!$mail) { + throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + fwrite($mail, 'To: ' . $toAddr . "\n"); + fwrite($mail, $header); + fwrite($mail, $body); + $result = pclose($mail); + $this->doCallback( + ($result == 0), + [$toAddr], + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From, + [] + ); + if (0 !== $result) { + throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + } else { + $mail = @popen($sendmail, 'w'); + if (!$mail) { + throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + fwrite($mail, $header); + fwrite($mail, $body); + $result = pclose($mail); + $this->doCallback( + ($result == 0), + $this->to, + $this->cc, + $this->bcc, + $this->Subject, + $body, + $this->From, + [] + ); + if (0 !== $result) { + throw new Exception($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + + return true; + } + + /** + * Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters. + * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows. + * + * @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report + * + * @param string $string The string to be validated + * + * @return bool + */ + protected static function isShellSafe($string) + { + // Future-proof + if (escapeshellcmd($string) !== $string + or !in_array(escapeshellarg($string), ["'$string'", "\"$string\""]) + ) { + return false; + } + + $length = strlen($string); + + for ($i = 0; $i < $length; ++$i) { + $c = $string[$i]; + + // All other characters have a special meaning in at least one common shell, including = and +. + // Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here. + // Note that this does permit non-Latin alphanumeric characters based on the current locale. + if (!ctype_alnum($c) && strpos('@_-.', $c) === false) { + return false; + } + } + + return true; + } + + /** + * Check whether a file path is of a permitted type. + * Used to reject URLs and phar files from functions that access local file paths, + * such as addAttachment. + * + * @param string $path A relative or absolute path to a file + * + * @return bool + */ + protected static function isPermittedPath($path) + { + return !preg_match('#^[a-z]+://#i', $path); + } + + /** + * Send mail using the PHP mail() function. + * + * @see http://www.php.net/manual/en/book.mail.php + * + * @param string $header The message headers + * @param string $body The message body + * + * @throws Exception + * + * @return bool + */ + protected function mailSend($header, $body) + { + $toArr = []; + foreach ($this->to as $toaddr) { + $toArr[] = $this->addrFormat($toaddr); + } + $to = implode(', ', $toArr); + + $params = null; + //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver + if (!empty($this->Sender) and static::validateAddress($this->Sender)) { + //A space after `-f` is optional, but there is a long history of its presence + //causing problems, so we don't use one + //Exim docs: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html + //Sendmail docs: http://www.sendmail.org/~ca/email/man/sendmail.html + //Qmail docs: http://www.qmail.org/man/man8/qmail-inject.html + //Example problem: https://www.drupal.org/node/1057954 + // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. + if (self::isShellSafe($this->Sender)) { + $params = sprintf('-f%s', $this->Sender); + } + } + if (!empty($this->Sender) and static::validateAddress($this->Sender)) { + $old_from = ini_get('sendmail_from'); + ini_set('sendmail_from', $this->Sender); + } + $result = false; + if ($this->SingleTo and count($toArr) > 1) { + foreach ($toArr as $toAddr) { + $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); + $this->doCallback($result, [$toAddr], $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); + } + } else { + $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); + $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); + } + if (isset($old_from)) { + ini_set('sendmail_from', $old_from); + } + if (!$result) { + throw new Exception($this->lang('instantiate'), self::STOP_CRITICAL); + } + + return true; + } + + /** + * Get an instance to use for SMTP operations. + * Override this function to load your own SMTP implementation, + * or set one with setSMTPInstance. + * + * @return SMTP + */ + public function getSMTPInstance() + { + if (!is_object($this->smtp)) { + $this->smtp = new SMTP(); + } + + return $this->smtp; + } + + /** + * Provide an instance to use for SMTP operations. + * + * @param SMTP $smtp + * + * @return SMTP + */ + public function setSMTPInstance(SMTP $smtp) + { + $this->smtp = $smtp; + + return $this->smtp; + } + + /** + * Send mail via SMTP. + * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. + * + * @see PHPMailer::setSMTPInstance() to use a different class. + * + * @uses \PHPMailer\PHPMailer\SMTP + * + * @param string $header The message headers + * @param string $body The message body + * + * @throws Exception + * + * @return bool + */ + protected function smtpSend($header, $body) + { + $bad_rcpt = []; + if (!$this->smtpConnect($this->SMTPOptions)) { + throw new Exception($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); + } + //Sender already validated in preSend() + if ('' == $this->Sender) { + $smtp_from = $this->From; + } else { + $smtp_from = $this->Sender; + } + if (!$this->smtp->mail($smtp_from)) { + $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); + throw new Exception($this->ErrorInfo, self::STOP_CRITICAL); + } + + $callbacks = []; + // Attempt to send to all recipients + foreach ([$this->to, $this->cc, $this->bcc] as $togroup) { + foreach ($togroup as $to) { + if (!$this->smtp->recipient($to[0], $this->dsn)) { + $error = $this->smtp->getError(); + $bad_rcpt[] = ['to' => $to[0], 'error' => $error['detail']]; + $isSent = false; + } else { + $isSent = true; + } + + $callbacks[] = ['issent'=>$isSent, 'to'=>$to[0]]; + } + } + + // Only send the DATA command if we have viable recipients + if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) { + throw new Exception($this->lang('data_not_accepted'), self::STOP_CRITICAL); + } + + $smtp_transaction_id = $this->smtp->getLastTransactionID(); + + if ($this->SMTPKeepAlive) { + $this->smtp->reset(); + } else { + $this->smtp->quit(); + $this->smtp->close(); + } + + foreach ($callbacks as $cb) { + $this->doCallback( + $cb['issent'], + [$cb['to']], + [], + [], + $this->Subject, + $body, + $this->From, + ['smtp_transaction_id' => $smtp_transaction_id] + ); + } + + //Create error message for any bad addresses + if (count($bad_rcpt) > 0) { + $errstr = ''; + foreach ($bad_rcpt as $bad) { + $errstr .= $bad['to'] . ': ' . $bad['error']; + } + throw new Exception( + $this->lang('recipients_failed') . $errstr, + self::STOP_CONTINUE + ); + } + + return true; + } + + /** + * Initiate a connection to an SMTP server. + * Returns false if the operation failed. + * + * @param array $options An array of options compatible with stream_context_create() + * + * @throws Exception + * + * @uses \PHPMailer\PHPMailer\SMTP + * + * @return bool + */ + public function smtpConnect($options = null) + { + if (null === $this->smtp) { + $this->smtp = $this->getSMTPInstance(); + } + + //If no options are provided, use whatever is set in the instance + if (null === $options) { + $options = $this->SMTPOptions; + } + + // Already connected? + if ($this->smtp->connected()) { + return true; + } + + $this->smtp->setTimeout($this->Timeout); + $this->smtp->setDebugLevel($this->SMTPDebug); + $this->smtp->setDebugOutput($this->Debugoutput); + $this->smtp->setVerp($this->do_verp); + $hosts = explode(';', $this->Host); + $lastexception = null; + + foreach ($hosts as $hostentry) { + $hostinfo = []; + if (!preg_match( + '/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*|\[[a-fA-F0-9:]+\]):?([0-9]*)$/', + trim($hostentry), + $hostinfo + )) { + static::edebug($this->lang('connect_host') . ' ' . $hostentry); + // Not a valid host entry + continue; + } + // $hostinfo[2]: optional ssl or tls prefix + // $hostinfo[3]: the hostname + // $hostinfo[4]: optional port number + // The host string prefix can temporarily override the current setting for SMTPSecure + // If it's not specified, the default value is used + + //Check the host name is a valid name or IP address before trying to use it + if (!static::isValidHost($hostinfo[3])) { + static::edebug($this->lang('connect_host') . ' ' . $hostentry); + continue; + } + $prefix = ''; + $secure = $this->SMTPSecure; + $tls = (static::ENCRYPTION_STARTTLS == $this->SMTPSecure); + if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and static::ENCRYPTION_SMTPS == $this->SMTPSecure)) { + $prefix = 'ssl://'; + $tls = false; // Can't have SSL and TLS at the same time + $secure = static::ENCRYPTION_SMTPS; + } elseif ('tls' == $hostinfo[2]) { + $tls = true; + // tls doesn't use a prefix + $secure = static::ENCRYPTION_STARTTLS; + } + //Do we need the OpenSSL extension? + $sslext = defined('OPENSSL_ALGO_SHA256'); + if (static::ENCRYPTION_STARTTLS === $secure or static::ENCRYPTION_SMTPS === $secure) { + //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled + if (!$sslext) { + throw new Exception($this->lang('extension_missing') . 'openssl', self::STOP_CRITICAL); + } + } + $host = $hostinfo[3]; + $port = $this->Port; + $tport = (int) $hostinfo[4]; + if ($tport > 0 and $tport < 65536) { + $port = $tport; + } + if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { + try { + if ($this->Helo) { + $hello = $this->Helo; + } else { + $hello = $this->serverHostname(); + } + $this->smtp->hello($hello); + //Automatically enable TLS encryption if: + // * it's not disabled + // * we have openssl extension + // * we are not already using SSL + // * the server offers STARTTLS + if ($this->SMTPAutoTLS and $sslext and 'ssl' != $secure and $this->smtp->getServerExt('STARTTLS')) { + $tls = true; + } + if ($tls) { + if (!$this->smtp->startTLS()) { + throw new Exception($this->lang('connect_host')); + } + // We must resend EHLO after TLS negotiation + $this->smtp->hello($hello); + } + if ($this->SMTPAuth) { + if (!$this->smtp->authenticate( + $this->Username, + $this->Password, + $this->AuthType, + $this->oauth + ) + ) { + throw new Exception($this->lang('authenticate')); + } + } + + return true; + } catch (Exception $exc) { + $lastexception = $exc; + $this->edebug($exc->getMessage()); + // We must have connected, but then failed TLS or Auth, so close connection nicely + $this->smtp->quit(); + } + } + } + // If we get here, all connection attempts have failed, so close connection hard + $this->smtp->close(); + // As we've caught all exceptions, just report whatever the last one was + if ($this->exceptions and null !== $lastexception) { + throw $lastexception; + } + + return false; + } + + /** + * Close the active SMTP session if one exists. + */ + public function smtpClose() + { + if (null !== $this->smtp) { + if ($this->smtp->connected()) { + $this->smtp->quit(); + $this->smtp->close(); + } + } + } + + /** + * Set the language for error messages. + * Returns false if it cannot load the language file. + * The default language is English. + * + * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") + * @param string $lang_path Path to the language file directory, with trailing separator (slash) + * + * @return bool + */ + public function setLanguage($langcode = 'en', $lang_path = '') + { + // Backwards compatibility for renamed language codes + $renamed_langcodes = [ + 'br' => 'pt_br', + 'cz' => 'cs', + 'dk' => 'da', + 'no' => 'nb', + 'se' => 'sv', + 'rs' => 'sr', + 'tg' => 'tl', + ]; + + if (isset($renamed_langcodes[$langcode])) { + $langcode = $renamed_langcodes[$langcode]; + } + + // Define full set of translatable strings in English + $PHPMAILER_LANG = [ + 'authenticate' => 'SMTP Error: Could not authenticate.', + 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', + 'data_not_accepted' => 'SMTP Error: data not accepted.', + 'empty_message' => 'Message body empty', + 'encoding' => 'Unknown encoding: ', + 'execute' => 'Could not execute: ', + 'file_access' => 'Could not access file: ', + 'file_open' => 'File Error: Could not open file: ', + 'from_failed' => 'The following From address failed: ', + 'instantiate' => 'Could not instantiate mail function.', + 'invalid_address' => 'Invalid address: ', + 'mailer_not_supported' => ' mailer is not supported.', + 'provide_address' => 'You must provide at least one recipient email address.', + 'recipients_failed' => 'SMTP Error: The following recipients failed: ', + 'signing' => 'Signing Error: ', + 'smtp_connect_failed' => 'SMTP connect() failed.', + 'smtp_error' => 'SMTP server error: ', + 'variable_set' => 'Cannot set or reset variable: ', + 'extension_missing' => 'Extension missing: ', + ]; + if (empty($lang_path)) { + // Calculate an absolute path so it can work if CWD is not here + $lang_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR; + } + //Validate $langcode + if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) { + $langcode = 'en'; + } + $foundlang = true; + $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; + // There is no English translation file + if ('en' != $langcode) { + // Make sure language file path is readable + if (!static::isPermittedPath($lang_file) || !file_exists($lang_file)) { + $foundlang = false; + } else { + // Overwrite language-specific strings. + // This way we'll never have missing translation keys. + $foundlang = include $lang_file; + } + } + $this->language = $PHPMAILER_LANG; + + return (bool) $foundlang; // Returns false if language not found + } + + /** + * Get the array of strings for the current language. + * + * @return array + */ + public function getTranslations() + { + return $this->language; + } + + /** + * Create recipient headers. + * + * @param string $type + * @param array $addr An array of recipients, + * where each recipient is a 2-element indexed array with element 0 containing an address + * and element 1 containing a name, like: + * [['joe@example.com', 'Joe User'], ['zoe@example.com', 'Zoe User']] + * + * @return string + */ + public function addrAppend($type, $addr) + { + $addresses = []; + foreach ($addr as $address) { + $addresses[] = $this->addrFormat($address); + } + + return $type . ': ' . implode(', ', $addresses) . static::$LE; + } + + /** + * Format an address for use in a message header. + * + * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name like + * ['joe@example.com', 'Joe User'] + * + * @return string + */ + public function addrFormat($addr) + { + if (empty($addr[1])) { // No name provided + return $this->secureHeader($addr[0]); + } + + return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader( + $addr[0] + ) . '>'; + } + + /** + * Word-wrap message. + * For use with mailers that do not automatically perform wrapping + * and for quoted-printable encoded messages. + * Original written by philippe. + * + * @param string $message The message to wrap + * @param int $length The line length to wrap to + * @param bool $qp_mode Whether to run in Quoted-Printable mode + * + * @return string + */ + public function wrapText($message, $length, $qp_mode = false) + { + if ($qp_mode) { + $soft_break = sprintf(' =%s', static::$LE); + } else { + $soft_break = static::$LE; + } + // If utf-8 encoding is used, we will need to make sure we don't + // split multibyte characters when we wrap + $is_utf8 = static::CHARSET_UTF8 === strtolower($this->CharSet); + $lelen = strlen(static::$LE); + $crlflen = strlen(static::$LE); + + $message = static::normalizeBreaks($message); + //Remove a trailing line break + if (substr($message, -$lelen) == static::$LE) { + $message = substr($message, 0, -$lelen); + } + + //Split message into lines + $lines = explode(static::$LE, $message); + //Message will be rebuilt in here + $message = ''; + foreach ($lines as $line) { + $words = explode(' ', $line); + $buf = ''; + $firstword = true; + foreach ($words as $word) { + if ($qp_mode and (strlen($word) > $length)) { + $space_left = $length - strlen($buf) - $crlflen; + if (!$firstword) { + if ($space_left > 20) { + $len = $space_left; + if ($is_utf8) { + $len = $this->utf8CharBoundary($word, $len); + } elseif ('=' == substr($word, $len - 1, 1)) { + --$len; + } elseif ('=' == substr($word, $len - 2, 1)) { + $len -= 2; + } + $part = substr($word, 0, $len); + $word = substr($word, $len); + $buf .= ' ' . $part; + $message .= $buf . sprintf('=%s', static::$LE); + } else { + $message .= $buf . $soft_break; + } + $buf = ''; + } + while (strlen($word) > 0) { + if ($length <= 0) { + break; + } + $len = $length; + if ($is_utf8) { + $len = $this->utf8CharBoundary($word, $len); + } elseif ('=' == substr($word, $len - 1, 1)) { + --$len; + } elseif ('=' == substr($word, $len - 2, 1)) { + $len -= 2; + } + $part = substr($word, 0, $len); + $word = substr($word, $len); + + if (strlen($word) > 0) { + $message .= $part . sprintf('=%s', static::$LE); + } else { + $buf = $part; + } + } + } else { + $buf_o = $buf; + if (!$firstword) { + $buf .= ' '; + } + $buf .= $word; + + if (strlen($buf) > $length and '' != $buf_o) { + $message .= $buf_o . $soft_break; + $buf = $word; + } + } + $firstword = false; + } + $message .= $buf . static::$LE; + } + + return $message; + } + + /** + * Find the last character boundary prior to $maxLength in a utf-8 + * quoted-printable encoded string. + * Original written by Colin Brown. + * + * @param string $encodedText utf-8 QP text + * @param int $maxLength Find the last character boundary prior to this length + * + * @return int + */ + public function utf8CharBoundary($encodedText, $maxLength) + { + $foundSplitPos = false; + $lookBack = 3; + while (!$foundSplitPos) { + $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); + $encodedCharPos = strpos($lastChunk, '='); + if (false !== $encodedCharPos) { + // Found start of encoded character byte within $lookBack block. + // Check the encoded byte value (the 2 chars after the '=') + $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); + $dec = hexdec($hex); + if ($dec < 128) { + // Single byte character. + // If the encoded char was found at pos 0, it will fit + // otherwise reduce maxLength to start of the encoded char + if ($encodedCharPos > 0) { + $maxLength -= $lookBack - $encodedCharPos; + } + $foundSplitPos = true; + } elseif ($dec >= 192) { + // First byte of a multi byte character + // Reduce maxLength to split at start of character + $maxLength -= $lookBack - $encodedCharPos; + $foundSplitPos = true; + } elseif ($dec < 192) { + // Middle byte of a multi byte character, look further back + $lookBack += 3; + } + } else { + // No encoded character found + $foundSplitPos = true; + } + } + + return $maxLength; + } + + /** + * Apply word wrapping to the message body. + * Wraps the message body to the number of chars set in the WordWrap property. + * You should only do this to plain-text bodies as wrapping HTML tags may break them. + * This is called automatically by createBody(), so you don't need to call it yourself. + */ + public function setWordWrap() + { + if ($this->WordWrap < 1) { + return; + } + + switch ($this->message_type) { + case 'alt': + case 'alt_inline': + case 'alt_attach': + case 'alt_inline_attach': + $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap); + break; + default: + $this->Body = $this->wrapText($this->Body, $this->WordWrap); + break; + } + } + + /** + * Assemble message headers. + * + * @return string The assembled headers + */ + public function createHeader() + { + $result = ''; + + $result .= $this->headerLine('Date', '' == $this->MessageDate ? self::rfcDate() : $this->MessageDate); + + // To be created automatically by mail() + if ($this->SingleTo) { + if ('mail' != $this->Mailer) { + foreach ($this->to as $toaddr) { + $this->SingleToArray[] = $this->addrFormat($toaddr); + } + } + } else { + if (count($this->to) > 0) { + if ('mail' != $this->Mailer) { + $result .= $this->addrAppend('To', $this->to); + } + } elseif (count($this->cc) == 0) { + $result .= $this->headerLine('To', 'undisclosed-recipients:;'); + } + } + + $result .= $this->addrAppend('From', [[trim($this->From), $this->FromName]]); + + // sendmail and mail() extract Cc from the header before sending + if (count($this->cc) > 0) { + $result .= $this->addrAppend('Cc', $this->cc); + } + + // sendmail and mail() extract Bcc from the header before sending + if (( + 'sendmail' == $this->Mailer or 'qmail' == $this->Mailer or 'mail' == $this->Mailer + ) + and count($this->bcc) > 0 + ) { + $result .= $this->addrAppend('Bcc', $this->bcc); + } + + if (count($this->ReplyTo) > 0) { + $result .= $this->addrAppend('Reply-To', $this->ReplyTo); + } + + // mail() sets the subject itself + if ('mail' != $this->Mailer) { + $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); + } + + // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4 + // https://tools.ietf.org/html/rfc5322#section-3.6.4 + if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) { + $this->lastMessageID = $this->MessageID; + } else { + $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname()); + } + $result .= $this->headerLine('Message-ID', $this->lastMessageID); + if (null !== $this->Priority) { + $result .= $this->headerLine('X-Priority', $this->Priority); + } + if ('' === $this->XMailer) { + $result .= $this->headerLine( + 'X-Mailer', + 'PHPMailer ' . self::VERSION . ' (https://github.com/PHPMailer/PHPMailer)' + ); + } else { + $myXmailer = trim($this->XMailer); + if ($myXmailer) { + $result .= $this->headerLine('X-Mailer', $myXmailer); + } + } + + if ('' != $this->ConfirmReadingTo) { + $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>'); + } + + // Add custom headers + foreach ($this->CustomHeader as $header) { + $result .= $this->headerLine( + trim($header[0]), + $this->encodeHeader(trim($header[1])) + ); + } + if (!$this->sign_key_file) { + $result .= $this->headerLine('MIME-Version', '1.0'); + $result .= $this->getMailMIME(); + } + + return $result; + } + + /** + * Get the message MIME type headers. + * + * @return string + */ + public function getMailMIME() + { + $result = ''; + $ismultipart = true; + switch ($this->message_type) { + case 'inline': + $result .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';'); + $result .= $this->textLine(' boundary="' . $this->boundary[1] . '"'); + break; + case 'attach': + case 'inline_attach': + case 'alt_attach': + case 'alt_inline_attach': + $result .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_MIXED . ';'); + $result .= $this->textLine(' boundary="' . $this->boundary[1] . '"'); + break; + case 'alt': + case 'alt_inline': + $result .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_ALTERNATIVE . ';'); + $result .= $this->textLine(' boundary="' . $this->boundary[1] . '"'); + break; + default: + // Catches case 'plain': and case '': + $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet); + $ismultipart = false; + break; + } + // RFC1341 part 5 says 7bit is assumed if not specified + if (static::ENCODING_7BIT != $this->Encoding) { + // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE + if ($ismultipart) { + if (static::ENCODING_8BIT == $this->Encoding) { + $result .= $this->headerLine('Content-Transfer-Encoding', static::ENCODING_8BIT); + } + // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible + } else { + $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); + } + } + + if ('mail' != $this->Mailer) { + $result .= static::$LE; + } + + return $result; + } + + /** + * Returns the whole MIME message. + * Includes complete headers and body. + * Only valid post preSend(). + * + * @see PHPMailer::preSend() + * + * @return string + */ + public function getSentMIMEMessage() + { + return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . static::$LE . static::$LE . $this->MIMEBody; + } + + /** + * Create a unique ID to use for boundaries. + * + * @return string + */ + protected function generateId() + { + $len = 32; //32 bytes = 256 bits + if (function_exists('random_bytes')) { + $bytes = random_bytes($len); + } elseif (function_exists('openssl_random_pseudo_bytes')) { + $bytes = openssl_random_pseudo_bytes($len); + } else { + //Use a hash to force the length to the same as the other methods + $bytes = hash('sha256', uniqid((string) mt_rand(), true), true); + } + + //We don't care about messing up base64 format here, just want a random string + return str_replace(['=', '+', '/'], '', base64_encode(hash('sha256', $bytes, true))); + } + + /** + * Assemble the message body. + * Returns an empty string on failure. + * + * @throws Exception + * + * @return string The assembled message body + */ + public function createBody() + { + $body = ''; + //Create unique IDs and preset boundaries + $this->uniqueid = $this->generateId(); + $this->boundary[1] = 'b1_' . $this->uniqueid; + $this->boundary[2] = 'b2_' . $this->uniqueid; + $this->boundary[3] = 'b3_' . $this->uniqueid; + + if ($this->sign_key_file) { + $body .= $this->getMailMIME() . static::$LE; + } + + $this->setWordWrap(); + + $bodyEncoding = $this->Encoding; + $bodyCharSet = $this->CharSet; + //Can we do a 7-bit downgrade? + if (static::ENCODING_8BIT == $bodyEncoding and !$this->has8bitChars($this->Body)) { + $bodyEncoding = static::ENCODING_7BIT; + //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit + $bodyCharSet = static::CHARSET_ASCII; + } + //If lines are too long, and we're not already using an encoding that will shorten them, + //change to quoted-printable transfer encoding for the body part only + if (static::ENCODING_BASE64 != $this->Encoding and static::hasLineLongerThanMax($this->Body)) { + $bodyEncoding = static::ENCODING_QUOTED_PRINTABLE; + } + + $altBodyEncoding = $this->Encoding; + $altBodyCharSet = $this->CharSet; + //Can we do a 7-bit downgrade? + if (static::ENCODING_8BIT == $altBodyEncoding and !$this->has8bitChars($this->AltBody)) { + $altBodyEncoding = static::ENCODING_7BIT; + //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit + $altBodyCharSet = static::CHARSET_ASCII; + } + //If lines are too long, and we're not already using an encoding that will shorten them, + //change to quoted-printable transfer encoding for the alt body part only + if (static::ENCODING_BASE64 != $altBodyEncoding and static::hasLineLongerThanMax($this->AltBody)) { + $altBodyEncoding = static::ENCODING_QUOTED_PRINTABLE; + } + //Use this as a preamble in all multipart message types + $mimepre = 'This is a multi-part message in MIME format.' . static::$LE; + switch ($this->message_type) { + case 'inline': + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= static::$LE; + $body .= $this->attachAll('inline', $this->boundary[1]); + break; + case 'attach': + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= static::$LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + case 'inline_attach': + $body .= $mimepre; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';'); + $body .= $this->textLine(' boundary="' . $this->boundary[2] . '";'); + $body .= $this->textLine(' type="' . static::CONTENT_TYPE_TEXT_HTML . '"'); + $body .= static::$LE; + $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= static::$LE; + $body .= $this->attachAll('inline', $this->boundary[2]); + $body .= static::$LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + case 'alt': + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, static::CONTENT_TYPE_PLAINTEXT, $altBodyEncoding); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); + $body .= static::$LE; + $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, static::CONTENT_TYPE_TEXT_HTML, $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= static::$LE; + if (!empty($this->Ical)) { + $method = static::ICAL_METHOD_REQUEST; + foreach (static::$IcalMethods as $imethod) { + if (stripos($this->Ical, 'METHOD:' . $imethod) !== false) { + $method = $imethod; + break; + } + } + $body .= $this->getBoundary($this->boundary[1], '', static::CONTENT_TYPE_TEXT_CALENDAR . '; method=' . $method, ''); + $body .= $this->encodeString($this->Ical, $this->Encoding); + $body .= static::$LE; + } + $body .= $this->endBoundary($this->boundary[1]); + break; + case 'alt_inline': + $body .= $mimepre; + $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, static::CONTENT_TYPE_PLAINTEXT, $altBodyEncoding); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); + $body .= static::$LE; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';'); + $body .= $this->textLine(' boundary="' . $this->boundary[2] . '";'); + $body .= $this->textLine(' type="' . static::CONTENT_TYPE_TEXT_HTML . '"'); + $body .= static::$LE; + $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, static::CONTENT_TYPE_TEXT_HTML, $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= static::$LE; + $body .= $this->attachAll('inline', $this->boundary[2]); + $body .= static::$LE; + $body .= $this->endBoundary($this->boundary[1]); + break; + case 'alt_attach': + $body .= $mimepre; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_ALTERNATIVE . ';'); + $body .= $this->textLine(' boundary="' . $this->boundary[2] . '"'); + $body .= static::$LE; + $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, static::CONTENT_TYPE_PLAINTEXT, $altBodyEncoding); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); + $body .= static::$LE; + $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, static::CONTENT_TYPE_TEXT_HTML, $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= static::$LE; + if (!empty($this->Ical)) { + $method = static::ICAL_METHOD_REQUEST; + foreach (static::$IcalMethods as $imethod) { + if (stripos($this->Ical, 'METHOD:' . $imethod) !== false) { + $method = $imethod; + break; + } + } + $body .= $this->getBoundary($this->boundary[2], '', static::CONTENT_TYPE_TEXT_CALENDAR . '; method=' . $method, ''); + $body .= $this->encodeString($this->Ical, $this->Encoding); + } + $body .= $this->endBoundary($this->boundary[2]); + $body .= static::$LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + case 'alt_inline_attach': + $body .= $mimepre; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_ALTERNATIVE . ';'); + $body .= $this->textLine(' boundary="' . $this->boundary[2] . '"'); + $body .= static::$LE; + $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, static::CONTENT_TYPE_PLAINTEXT, $altBodyEncoding); + $body .= $this->encodeString($this->AltBody, $altBodyEncoding); + $body .= static::$LE; + $body .= $this->textLine('--' . $this->boundary[2]); + $body .= $this->headerLine('Content-Type', static::CONTENT_TYPE_MULTIPART_RELATED . ';'); + $body .= $this->textLine(' boundary="' . $this->boundary[3] . '";'); + $body .= $this->textLine(' type="' . static::CONTENT_TYPE_TEXT_HTML . '"'); + $body .= static::$LE; + $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, static::CONTENT_TYPE_TEXT_HTML, $bodyEncoding); + $body .= $this->encodeString($this->Body, $bodyEncoding); + $body .= static::$LE; + $body .= $this->attachAll('inline', $this->boundary[3]); + $body .= static::$LE; + $body .= $this->endBoundary($this->boundary[2]); + $body .= static::$LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + default: + // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types + //Reset the `Encoding` property in case we changed it for line length reasons + $this->Encoding = $bodyEncoding; + $body .= $this->encodeString($this->Body, $this->Encoding); + break; + } + + if ($this->isError()) { + $body = ''; + if ($this->exceptions) { + throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL); + } + } elseif ($this->sign_key_file) { + try { + if (!defined('PKCS7_TEXT')) { + throw new Exception($this->lang('extension_missing') . 'openssl'); + } + + $file = tempnam(sys_get_temp_dir(), 'srcsign'); + $signed = tempnam(sys_get_temp_dir(), 'mailsign'); + file_put_contents($file, $body); + + //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197 + if (empty($this->sign_extracerts_file)) { + $sign = @openssl_pkcs7_sign( + $file, + $signed, + 'file://' . realpath($this->sign_cert_file), + ['file://' . realpath($this->sign_key_file), $this->sign_key_pass], + [] + ); + } else { + $sign = @openssl_pkcs7_sign( + $file, + $signed, + 'file://' . realpath($this->sign_cert_file), + ['file://' . realpath($this->sign_key_file), $this->sign_key_pass], + [], + PKCS7_DETACHED, + $this->sign_extracerts_file + ); + } + + @unlink($file); + if ($sign) { + $body = file_get_contents($signed); + @unlink($signed); + //The message returned by openssl contains both headers and body, so need to split them up + $parts = explode("\n\n", $body, 2); + $this->MIMEHeader .= $parts[0] . static::$LE . static::$LE; + $body = $parts[1]; + } else { + @unlink($signed); + throw new Exception($this->lang('signing') . openssl_error_string()); + } + } catch (Exception $exc) { + $body = ''; + if ($this->exceptions) { + throw $exc; + } + } + } + + return $body; + } + + /** + * Return the start of a message boundary. + * + * @param string $boundary + * @param string $charSet + * @param string $contentType + * @param string $encoding + * + * @return string + */ + protected function getBoundary($boundary, $charSet, $contentType, $encoding) + { + $result = ''; + if ('' == $charSet) { + $charSet = $this->CharSet; + } + if ('' == $contentType) { + $contentType = $this->ContentType; + } + if ('' == $encoding) { + $encoding = $this->Encoding; + } + $result .= $this->textLine('--' . $boundary); + $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet); + $result .= static::$LE; + // RFC1341 part 5 says 7bit is assumed if not specified + if (static::ENCODING_7BIT != $encoding) { + $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); + } + $result .= static::$LE; + + return $result; + } + + /** + * Return the end of a message boundary. + * + * @param string $boundary + * + * @return string + */ + protected function endBoundary($boundary) + { + return static::$LE . '--' . $boundary . '--' . static::$LE; + } + + /** + * Set the message type. + * PHPMailer only supports some preset message types, not arbitrary MIME structures. + */ + protected function setMessageType() + { + $type = []; + if ($this->alternativeExists()) { + $type[] = 'alt'; + } + if ($this->inlineImageExists()) { + $type[] = 'inline'; + } + if ($this->attachmentExists()) { + $type[] = 'attach'; + } + $this->message_type = implode('_', $type); + if ('' == $this->message_type) { + //The 'plain' message_type refers to the message having a single body element, not that it is plain-text + $this->message_type = 'plain'; + } + } + + /** + * Format a header line. + * + * @param string $name + * @param string|int $value + * + * @return string + */ + public function headerLine($name, $value) + { + return $name . ': ' . $value . static::$LE; + } + + /** + * Return a formatted mail line. + * + * @param string $value + * + * @return string + */ + public function textLine($value) + { + return $value . static::$LE; + } + + /** + * Add an attachment from a path on the filesystem. + * Never use a user-supplied path to a file! + * Returns false if the file could not be found or read. + * Explicitly *does not* support passing URLs; PHPMailer is not an HTTP client. + * If you need to do that, fetch the resource yourself and pass it in via a local file or string. + * + * @param string $path Path to the attachment + * @param string $name Overrides the attachment name + * @param string $encoding File encoding (see $Encoding) + * @param string $type File extension (MIME) type + * @param string $disposition Disposition to use + * + * @throws Exception + * + * @return bool + */ + public function addAttachment( + $path, + $name = '', + $encoding = self::ENCODING_BASE64, + $type = '', + $disposition = 'attachment' + ) { + try { + if (!static::isPermittedPath($path) || !@is_file($path)) { + throw new Exception($this->lang('file_access') . $path, self::STOP_CONTINUE); + } + + // If a MIME type is not specified, try to work it out from the file name + if ('' == $type) { + $type = static::filenameToType($path); + } + + $filename = static::mb_pathinfo($path, PATHINFO_BASENAME); + if ('' == $name) { + $name = $filename; + } + + if (!$this->validateEncoding($encoding)) { + throw new Exception($this->lang('encoding') . $encoding); + } + + $this->attachment[] = [ + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => $disposition, + 7 => $name, + ]; + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return false; + } + + return true; + } + + /** + * Return the array of attachments. + * + * @return array + */ + public function getAttachments() + { + return $this->attachment; + } + + /** + * Attach all file, string, and binary attachments to the message. + * Returns an empty string on failure. + * + * @param string $disposition_type + * @param string $boundary + * + * @return string + */ + protected function attachAll($disposition_type, $boundary) + { + // Return text of body + $mime = []; + $cidUniq = []; + $incl = []; + + // Add all attachments + foreach ($this->attachment as $attachment) { + // Check if it is a valid disposition_filter + if ($attachment[6] == $disposition_type) { + // Check for string attachment + $string = ''; + $path = ''; + $bString = $attachment[5]; + if ($bString) { + $string = $attachment[0]; + } else { + $path = $attachment[0]; + } + + $inclhash = hash('sha256', serialize($attachment)); + if (in_array($inclhash, $incl)) { + continue; + } + $incl[] = $inclhash; + $name = $attachment[2]; + $encoding = $attachment[3]; + $type = $attachment[4]; + $disposition = $attachment[6]; + $cid = $attachment[7]; + if ('inline' == $disposition and array_key_exists($cid, $cidUniq)) { + continue; + } + $cidUniq[$cid] = true; + + $mime[] = sprintf('--%s%s', $boundary, static::$LE); + //Only include a filename property if we have one + if (!empty($name)) { + $mime[] = sprintf( + 'Content-Type: %s; name="%s"%s', + $type, + $this->encodeHeader($this->secureHeader($name)), + static::$LE + ); + } else { + $mime[] = sprintf( + 'Content-Type: %s%s', + $type, + static::$LE + ); + } + // RFC1341 part 5 says 7bit is assumed if not specified + if (static::ENCODING_7BIT != $encoding) { + $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, static::$LE); + } + + if (!empty($cid)) { + $mime[] = sprintf( + 'Content-ID: <%s>%s', + $this->encodeHeader($this->secureHeader($cid)), + static::$LE + ); + } + + // If a filename contains any of these chars, it should be quoted, + // but not otherwise: RFC2183 & RFC2045 5.1 + // Fixes a warning in IETF's msglint MIME checker + // Allow for bypassing the Content-Disposition header totally + if (!empty($disposition)) { + $encoded_name = $this->encodeHeader($this->secureHeader($name)); + if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) { + $mime[] = sprintf( + 'Content-Disposition: %s; filename="%s"%s', + $disposition, + $encoded_name, + static::$LE . static::$LE + ); + } else { + if (!empty($encoded_name)) { + $mime[] = sprintf( + 'Content-Disposition: %s; filename=%s%s', + $disposition, + $encoded_name, + static::$LE . static::$LE + ); + } else { + $mime[] = sprintf( + 'Content-Disposition: %s%s', + $disposition, + static::$LE . static::$LE + ); + } + } + } else { + $mime[] = static::$LE; + } + + // Encode as string attachment + if ($bString) { + $mime[] = $this->encodeString($string, $encoding); + } else { + $mime[] = $this->encodeFile($path, $encoding); + } + if ($this->isError()) { + return ''; + } + $mime[] = static::$LE; + } + } + + $mime[] = sprintf('--%s--%s', $boundary, static::$LE); + + return implode('', $mime); + } + + /** + * Encode a file attachment in requested format. + * Returns an empty string on failure. + * + * @param string $path The full path to the file + * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' + * + * @throws Exception + * + * @return string + */ + protected function encodeFile($path, $encoding = self::ENCODING_BASE64) + { + try { + if (!static::isPermittedPath($path) || !file_exists($path)) { + throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE); + } + $file_buffer = file_get_contents($path); + if (false === $file_buffer) { + throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE); + } + $file_buffer = $this->encodeString($file_buffer, $encoding); + + return $file_buffer; + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + + return ''; + } + } + + /** + * Encode a string in requested format. + * Returns an empty string on failure. + * + * @param string $str The text to encode + * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' + * + * @throws Exception + * + * @return string + */ + public function encodeString($str, $encoding = self::ENCODING_BASE64) + { + $encoded = ''; + switch (strtolower($encoding)) { + case static::ENCODING_BASE64: + $encoded = chunk_split( + base64_encode($str), + static::STD_LINE_LENGTH, + static::$LE + ); + break; + case static::ENCODING_7BIT: + case static::ENCODING_8BIT: + $encoded = static::normalizeBreaks($str); + // Make sure it ends with a line break + if (substr($encoded, -(strlen(static::$LE))) != static::$LE) { + $encoded .= static::$LE; + } + break; + case static::ENCODING_BINARY: + $encoded = $str; + break; + case static::ENCODING_QUOTED_PRINTABLE: + $encoded = $this->encodeQP($str); + break; + default: + $this->setError($this->lang('encoding') . $encoding); + if ($this->exceptions) { + throw new Exception($this->lang('encoding') . $encoding); + } + break; + } + + return $encoded; + } + + /** + * Encode a header value (not including its label) optimally. + * Picks shortest of Q, B, or none. Result includes folding if needed. + * See RFC822 definitions for phrase, comment and text positions. + * + * @param string $str The header value to encode + * @param string $position What context the string will be used in + * + * @return string + */ + public function encodeHeader($str, $position = 'text') + { + $matchcount = 0; + switch (strtolower($position)) { + case 'phrase': + if (!preg_match('/[\200-\377]/', $str)) { + // Can't use addslashes as we don't know the value of magic_quotes_sybase + $encoded = addcslashes($str, "\0..\37\177\\\""); + if (($str == $encoded) and !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { + return $encoded; + } + + return "\"$encoded\""; + } + $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); + break; + /* @noinspection PhpMissingBreakStatementInspection */ + case 'comment': + $matchcount = preg_match_all('/[()"]/', $str, $matches); + //fallthrough + case 'text': + default: + $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); + break; + } + + if ($this->has8bitChars($str)) { + $charset = $this->CharSet; + } else { + $charset = static::CHARSET_ASCII; + } + + // Q/B encoding adds 8 chars and the charset ("` =??[QB]??=`"). + $overhead = 8 + strlen($charset); + + if ('mail' == $this->Mailer) { + $maxlen = static::MAIL_MAX_LINE_LENGTH - $overhead; + } else { + $maxlen = static::STD_LINE_LENGTH - $overhead; + } + + // Select the encoding that produces the shortest output and/or prevents corruption. + if ($matchcount > strlen($str) / 3) { + // More than 1/3 of the content needs encoding, use B-encode. + $encoding = 'B'; + } elseif ($matchcount > 0) { + // Less than 1/3 of the content needs encoding, use Q-encode. + $encoding = 'Q'; + } elseif (strlen($str) > $maxlen) { + // No encoding needed, but value exceeds max line length, use Q-encode to prevent corruption. + $encoding = 'Q'; + } else { + // No reformatting needed + $encoding = false; + } + + switch ($encoding) { + case 'B': + if ($this->hasMultiBytes($str)) { + // Use a custom function which correctly encodes and wraps long + // multibyte strings without breaking lines within a character + $encoded = $this->base64EncodeWrapMB($str, "\n"); + } else { + $encoded = base64_encode($str); + $maxlen -= $maxlen % 4; + $encoded = trim(chunk_split($encoded, $maxlen, "\n")); + } + $encoded = preg_replace('/^(.*)$/m', ' =?' . $charset . "?$encoding?\\1?=", $encoded); + break; + case 'Q': + $encoded = $this->encodeQ($str, $position); + $encoded = $this->wrapText($encoded, $maxlen, true); + $encoded = str_replace('=' . static::$LE, "\n", trim($encoded)); + $encoded = preg_replace('/^(.*)$/m', ' =?' . $charset . "?$encoding?\\1?=", $encoded); + break; + default: + return $str; + } + + return trim(static::normalizeBreaks($encoded)); + } + + /** + * Check if a string contains multi-byte characters. + * + * @param string $str multi-byte text to wrap encode + * + * @return bool + */ + public function hasMultiBytes($str) + { + if (function_exists('mb_strlen')) { + return strlen($str) > mb_strlen($str, $this->CharSet); + } + + // Assume no multibytes (we can't handle without mbstring functions anyway) + return false; + } + + /** + * Does a string contain any 8-bit chars (in any charset)? + * + * @param string $text + * + * @return bool + */ + public function has8bitChars($text) + { + return (bool) preg_match('/[\x80-\xFF]/', $text); + } + + /** + * Encode and wrap long multibyte strings for mail headers + * without breaking lines within a character. + * Adapted from a function by paravoid. + * + * @see http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283 + * + * @param string $str multi-byte text to wrap encode + * @param string $linebreak string to use as linefeed/end-of-line + * + * @return string + */ + public function base64EncodeWrapMB($str, $linebreak = null) + { + $start = '=?' . $this->CharSet . '?B?'; + $end = '?='; + $encoded = ''; + if (null === $linebreak) { + $linebreak = static::$LE; + } + + $mb_length = mb_strlen($str, $this->CharSet); + // Each line must have length <= 75, including $start and $end + $length = 75 - strlen($start) - strlen($end); + // Average multi-byte ratio + $ratio = $mb_length / strlen($str); + // Base64 has a 4:3 ratio + $avgLength = floor($length * $ratio * .75); + + for ($i = 0; $i < $mb_length; $i += $offset) { + $lookBack = 0; + do { + $offset = $avgLength - $lookBack; + $chunk = mb_substr($str, $i, $offset, $this->CharSet); + $chunk = base64_encode($chunk); + ++$lookBack; + } while (strlen($chunk) > $length); + $encoded .= $chunk . $linebreak; + } + + // Chomp the last linefeed + return substr($encoded, 0, -strlen($linebreak)); + } + + /** + * Encode a string in quoted-printable format. + * According to RFC2045 section 6.7. + * + * @param string $string The text to encode + * + * @return string + */ + public function encodeQP($string) + { + return static::normalizeBreaks(quoted_printable_encode($string)); + } + + /** + * Encode a string using Q encoding. + * + * @see http://tools.ietf.org/html/rfc2047#section-4.2 + * + * @param string $str the text to encode + * @param string $position Where the text is going to be used, see the RFC for what that means + * + * @return string + */ + public function encodeQ($str, $position = 'text') + { + // There should not be any EOL in the string + $pattern = ''; + $encoded = str_replace(["\r", "\n"], '', $str); + switch (strtolower($position)) { + case 'phrase': + // RFC 2047 section 5.3 + $pattern = '^A-Za-z0-9!*+\/ -'; + break; + /* + * RFC 2047 section 5.2. + * Build $pattern without including delimiters and [] + */ + /* @noinspection PhpMissingBreakStatementInspection */ + case 'comment': + $pattern = '\(\)"'; + /* Intentional fall through */ + case 'text': + default: + // RFC 2047 section 5.1 + // Replace every high ascii, control, =, ? and _ characters + /** @noinspection SuspiciousAssignmentsInspection */ + $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern; + break; + } + $matches = []; + if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) { + // If the string contains an '=', make sure it's the first thing we replace + // so as to avoid double-encoding + $eqkey = array_search('=', $matches[0]); + if (false !== $eqkey) { + unset($matches[0][$eqkey]); + array_unshift($matches[0], '='); + } + foreach (array_unique($matches[0]) as $char) { + $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded); + } + } + // Replace spaces with _ (more readable than =20) + // RFC 2047 section 4.2(2) + return str_replace(' ', '_', $encoded); + } + + /** + * Add a string or binary attachment (non-filesystem). + * This method can be used to attach ascii or binary data, + * such as a BLOB record from a database. + * + * @param string $string String attachment data + * @param string $filename Name of the attachment + * @param string $encoding File encoding (see $Encoding) + * @param string $type File extension (MIME) type + * @param string $disposition Disposition to use + * + * @throws Exception + * + * @return bool True on successfully adding an attachment + */ + public function addStringAttachment( + $string, + $filename, + $encoding = self::ENCODING_BASE64, + $type = '', + $disposition = 'attachment' + ) { + try { + // If a MIME type is not specified, try to work it out from the file name + if ('' == $type) { + $type = static::filenameToType($filename); + } + + if (!$this->validateEncoding($encoding)) { + throw new Exception($this->lang('encoding') . $encoding); + } + + // Append to $attachment array + $this->attachment[] = [ + 0 => $string, + 1 => $filename, + 2 => static::mb_pathinfo($filename, PATHINFO_BASENAME), + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => $disposition, + 7 => 0, + ]; + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return false; + } + + return true; + } + + /** + * Add an embedded (inline) attachment from a file. + * This can include images, sounds, and just about any other document type. + * These differ from 'regular' attachments in that they are intended to be + * displayed inline with the message, not just attached for download. + * This is used in HTML messages that embed the images + * the HTML refers to using the $cid value. + * Never use a user-supplied path to a file! + * + * @param string $path Path to the attachment + * @param string $cid Content ID of the attachment; Use this to reference + * the content when using an embedded image in HTML + * @param string $name Overrides the attachment name + * @param string $encoding File encoding (see $Encoding) + * @param string $type File MIME type + * @param string $disposition Disposition to use + * + * @throws Exception + * + * @return bool True on successfully adding an attachment + */ + public function addEmbeddedImage( + $path, + $cid, + $name = '', + $encoding = self::ENCODING_BASE64, + $type = '', + $disposition = 'inline' + ) { + try { + if (!static::isPermittedPath($path) || !@is_file($path)) { + throw new Exception($this->lang('file_access') . $path, self::STOP_CONTINUE); + } + + // If a MIME type is not specified, try to work it out from the file name + if ('' == $type) { + $type = static::filenameToType($path); + } + + if (!$this->validateEncoding($encoding)) { + throw new Exception($this->lang('encoding') . $encoding); + } + + $filename = static::mb_pathinfo($path, PATHINFO_BASENAME); + if ('' == $name) { + $name = $filename; + } + + // Append to $attachment array + $this->attachment[] = [ + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => $disposition, + 7 => $cid, + ]; + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return false; + } + + return true; + } + + /** + * Add an embedded stringified attachment. + * This can include images, sounds, and just about any other document type. + * If your filename doesn't contain an extension, be sure to set the $type to an appropriate MIME type. + * + * @param string $string The attachment binary data + * @param string $cid Content ID of the attachment; Use this to reference + * the content when using an embedded image in HTML + * @param string $name A filename for the attachment. If this contains an extension, + * PHPMailer will attempt to set a MIME type for the attachment. + * For example 'file.jpg' would get an 'image/jpeg' MIME type. + * @param string $encoding File encoding (see $Encoding), defaults to 'base64' + * @param string $type MIME type - will be used in preference to any automatically derived type + * @param string $disposition Disposition to use + * + * @throws Exception + * + * @return bool True on successfully adding an attachment + */ + public function addStringEmbeddedImage( + $string, + $cid, + $name = '', + $encoding = self::ENCODING_BASE64, + $type = '', + $disposition = 'inline' + ) { + try { + // If a MIME type is not specified, try to work it out from the name + if ('' == $type and !empty($name)) { + $type = static::filenameToType($name); + } + + if (!$this->validateEncoding($encoding)) { + throw new Exception($this->lang('encoding') . $encoding); + } + + // Append to $attachment array + $this->attachment[] = [ + 0 => $string, + 1 => $name, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => $disposition, + 7 => $cid, + ]; + } catch (Exception $exc) { + $this->setError($exc->getMessage()); + $this->edebug($exc->getMessage()); + if ($this->exceptions) { + throw $exc; + } + + return false; + } + + return true; + } + + /** + * Validate encodings. + * + * @param $encoding + * + * @return bool + */ + protected function validateEncoding($encoding) + { + return in_array( + $encoding, + [ + self::ENCODING_7BIT, + self::ENCODING_QUOTED_PRINTABLE, + self::ENCODING_BASE64, + self::ENCODING_8BIT, + self::ENCODING_BINARY, + ], + true + ); + } + + /** + * Check if an embedded attachment is present with this cid. + * + * @param string $cid + * + * @return bool + */ + protected function cidExists($cid) + { + foreach ($this->attachment as $attachment) { + if ('inline' == $attachment[6] and $cid == $attachment[7]) { + return true; + } + } + + return false; + } + + /** + * Check if an inline attachment is present. + * + * @return bool + */ + public function inlineImageExists() + { + foreach ($this->attachment as $attachment) { + if ('inline' == $attachment[6]) { + return true; + } + } + + return false; + } + + /** + * Check if an attachment (non-inline) is present. + * + * @return bool + */ + public function attachmentExists() + { + foreach ($this->attachment as $attachment) { + if ('attachment' == $attachment[6]) { + return true; + } + } + + return false; + } + + /** + * Check if this message has an alternative body set. + * + * @return bool + */ + public function alternativeExists() + { + return !empty($this->AltBody); + } + + /** + * Clear queued addresses of given kind. + * + * @param string $kind 'to', 'cc', or 'bcc' + */ + public function clearQueuedAddresses($kind) + { + $this->RecipientsQueue = array_filter( + $this->RecipientsQueue, + function ($params) use ($kind) { + return $params[0] != $kind; + } + ); + } + + /** + * Clear all To recipients. + */ + public function clearAddresses() + { + foreach ($this->to as $to) { + unset($this->all_recipients[strtolower($to[0])]); + } + $this->to = []; + $this->clearQueuedAddresses('to'); + } + + /** + * Clear all CC recipients. + */ + public function clearCCs() + { + foreach ($this->cc as $cc) { + unset($this->all_recipients[strtolower($cc[0])]); + } + $this->cc = []; + $this->clearQueuedAddresses('cc'); + } + + /** + * Clear all BCC recipients. + */ + public function clearBCCs() + { + foreach ($this->bcc as $bcc) { + unset($this->all_recipients[strtolower($bcc[0])]); + } + $this->bcc = []; + $this->clearQueuedAddresses('bcc'); + } + + /** + * Clear all ReplyTo recipients. + */ + public function clearReplyTos() + { + $this->ReplyTo = []; + $this->ReplyToQueue = []; + } + + /** + * Clear all recipient types. + */ + public function clearAllRecipients() + { + $this->to = []; + $this->cc = []; + $this->bcc = []; + $this->all_recipients = []; + $this->RecipientsQueue = []; + } + + /** + * Clear all filesystem, string, and binary attachments. + */ + public function clearAttachments() + { + $this->attachment = []; + } + + /** + * Clear all custom headers. + */ + public function clearCustomHeaders() + { + $this->CustomHeader = []; + } + + /** + * Add an error message to the error container. + * + * @param string $msg + */ + protected function setError($msg) + { + ++$this->error_count; + if ('smtp' == $this->Mailer and null !== $this->smtp) { + $lasterror = $this->smtp->getError(); + if (!empty($lasterror['error'])) { + $msg .= $this->lang('smtp_error') . $lasterror['error']; + if (!empty($lasterror['detail'])) { + $msg .= ' Detail: ' . $lasterror['detail']; + } + if (!empty($lasterror['smtp_code'])) { + $msg .= ' SMTP code: ' . $lasterror['smtp_code']; + } + if (!empty($lasterror['smtp_code_ex'])) { + $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex']; + } + } + } + $this->ErrorInfo = $msg; + } + + /** + * Return an RFC 822 formatted date. + * + * @return string + */ + public static function rfcDate() + { + // Set the time zone to whatever the default is to avoid 500 errors + // Will default to UTC if it's not set properly in php.ini + date_default_timezone_set(@date_default_timezone_get()); + + return date('D, j M Y H:i:s O'); + } + + /** + * Get the server hostname. + * Returns 'localhost.localdomain' if unknown. + * + * @return string + */ + protected function serverHostname() + { + $result = ''; + if (!empty($this->Hostname)) { + $result = $this->Hostname; + } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER)) { + $result = $_SERVER['SERVER_NAME']; + } elseif (function_exists('gethostname') and gethostname() !== false) { + $result = gethostname(); + } elseif (php_uname('n') !== false) { + $result = php_uname('n'); + } + if (!static::isValidHost($result)) { + return 'localhost.localdomain'; + } + + return $result; + } + + /** + * Validate whether a string contains a valid value to use as a hostname or IP address. + * IPv6 addresses must include [], e.g. `[::1]`, not just `::1`. + * + * @param string $host The host name or IP address to check + * + * @return bool + */ + public static function isValidHost($host) + { + //Simple syntax limits + if (empty($host) + or !is_string($host) + or strlen($host) > 256 + ) { + return false; + } + //Looks like a bracketed IPv6 address + if (trim($host, '[]') != $host) { + return (bool) filter_var(trim($host, '[]'), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6); + } + //If removing all the dots results in a numeric string, it must be an IPv4 address. + //Need to check this first because otherwise things like `999.0.0.0` are considered valid host names + if (is_numeric(str_replace('.', '', $host))) { + //Is it a valid IPv4 address? + return (bool) filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4); + } + if (filter_var('http://' . $host, FILTER_VALIDATE_URL)) { + //Is it a syntactically valid hostname? + return true; + } + + return false; + } + + /** + * Get an error message in the current language. + * + * @param string $key + * + * @return string + */ + protected function lang($key) + { + if (count($this->language) < 1) { + $this->setLanguage('en'); // set the default language + } + + if (array_key_exists($key, $this->language)) { + if ('smtp_connect_failed' == $key) { + //Include a link to troubleshooting docs on SMTP connection failure + //this is by far the biggest cause of support questions + //but it's usually not PHPMailer's fault. + return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting'; + } + + return $this->language[$key]; + } + + //Return the key as a fallback + return $key; + } + + /** + * Check if an error occurred. + * + * @return bool True if an error did occur + */ + public function isError() + { + return $this->error_count > 0; + } + + /** + * Add a custom header. + * $name value can be overloaded to contain + * both header name and value (name:value). + * + * @param string $name Custom header name + * @param string|null $value Header value + */ + public function addCustomHeader($name, $value = null) + { + if (null === $value) { + // Value passed in as name:value + $this->CustomHeader[] = explode(':', $name, 2); + } else { + $this->CustomHeader[] = [$name, $value]; + } + } + + /** + * Returns all custom headers. + * + * @return array + */ + public function getCustomHeaders() + { + return $this->CustomHeader; + } + + /** + * Create a message body from an HTML string. + * Automatically inlines images and creates a plain-text version by converting the HTML, + * overwriting any existing values in Body and AltBody. + * Do not source $message content from user input! + * $basedir is prepended when handling relative URLs, e.g. and must not be empty + * will look for an image file in $basedir/images/a.png and convert it to inline. + * If you don't provide a $basedir, relative paths will be left untouched (and thus probably break in email) + * Converts data-uri images into embedded attachments. + * If you don't want to apply these transformations to your HTML, just set Body and AltBody directly. + * + * @param string $message HTML message string + * @param string $basedir Absolute path to a base directory to prepend to relative paths to images + * @param bool|callable $advanced Whether to use the internal HTML to text converter + * or your own custom converter @see PHPMailer::html2text() + * + * @return string $message The transformed message Body + */ + public function msgHTML($message, $basedir = '', $advanced = false) + { + preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images); + if (array_key_exists(2, $images)) { + if (strlen($basedir) > 1 && '/' != substr($basedir, -1)) { + // Ensure $basedir has a trailing / + $basedir .= '/'; + } + foreach ($images[2] as $imgindex => $url) { + // Convert data URIs into embedded images + //e.g. "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" + if (preg_match('#^data:(image/(?:jpe?g|gif|png));?(base64)?,(.+)#', $url, $match)) { + if (count($match) == 4 and static::ENCODING_BASE64 == $match[2]) { + $data = base64_decode($match[3]); + } elseif ('' == $match[2]) { + $data = rawurldecode($match[3]); + } else { + //Not recognised so leave it alone + continue; + } + //Hash the decoded data, not the URL so that the same data-URI image used in multiple places + //will only be embedded once, even if it used a different encoding + $cid = hash('sha256', $data) . '@phpmailer.0'; // RFC2392 S 2 + + if (!$this->cidExists($cid)) { + $this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, static::ENCODING_BASE64, $match[1]); + } + $message = str_replace( + $images[0][$imgindex], + $images[1][$imgindex] . '="cid:' . $cid . '"', + $message + ); + continue; + } + if (// Only process relative URLs if a basedir is provided (i.e. no absolute local paths) + !empty($basedir) + // Ignore URLs containing parent dir traversal (..) + and (strpos($url, '..') === false) + // Do not change urls that are already inline images + and 0 !== strpos($url, 'cid:') + // Do not change absolute URLs, including anonymous protocol + and !preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url) + ) { + $filename = static::mb_pathinfo($url, PATHINFO_BASENAME); + $directory = dirname($url); + if ('.' == $directory) { + $directory = ''; + } + $cid = hash('sha256', $url) . '@phpmailer.0'; // RFC2392 S 2 + if (strlen($basedir) > 1 and '/' != substr($basedir, -1)) { + $basedir .= '/'; + } + if (strlen($directory) > 1 and '/' != substr($directory, -1)) { + $directory .= '/'; + } + if ($this->addEmbeddedImage( + $basedir . $directory . $filename, + $cid, + $filename, + static::ENCODING_BASE64, + static::_mime_types((string) static::mb_pathinfo($filename, PATHINFO_EXTENSION)) + ) + ) { + $message = preg_replace( + '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui', + $images[1][$imgindex] . '="cid:' . $cid . '"', + $message + ); + } + } + } + } + $this->isHTML(true); + // Convert all message body line breaks to LE, makes quoted-printable encoding work much better + $this->Body = static::normalizeBreaks($message); + $this->AltBody = static::normalizeBreaks($this->html2text($message, $advanced)); + if (!$this->alternativeExists()) { + $this->AltBody = 'This is an HTML-only message. To view it, activate HTML in your email application.' + . static::$LE; + } + + return $this->Body; + } + + /** + * Convert an HTML string into plain text. + * This is used by msgHTML(). + * Note - older versions of this function used a bundled advanced converter + * which was removed for license reasons in #232. + * Example usage: + * + * ```php + * // Use default conversion + * $plain = $mail->html2text($html); + * // Use your own custom converter + * $plain = $mail->html2text($html, function($html) { + * $converter = new MyHtml2text($html); + * return $converter->get_text(); + * }); + * ``` + * + * @param string $html The HTML text to convert + * @param bool|callable $advanced Any boolean value to use the internal converter, + * or provide your own callable for custom conversion + * + * @return string + */ + public function html2text($html, $advanced = false) + { + if (is_callable($advanced)) { + return call_user_func($advanced, $html); + } + + return html_entity_decode( + trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), + ENT_QUOTES, + $this->CharSet + ); + } + + /** + * Get the MIME type for a file extension. + * + * @param string $ext File extension + * + * @return string MIME type of file + */ + public static function _mime_types($ext = '') + { + $mimes = [ + 'xl' => 'application/excel', + 'js' => 'application/javascript', + 'hqx' => 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'bin' => 'application/macbinary', + 'doc' => 'application/msword', + 'word' => 'application/msword', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', + 'class' => 'application/octet-stream', + 'dll' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'exe' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'psd' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'so' => 'application/octet-stream', + 'oda' => 'application/oda', + 'pdf' => 'application/pdf', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'mif' => 'application/vnd.mif', + 'xls' => 'application/vnd.ms-excel', + 'ppt' => 'application/vnd.ms-powerpoint', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'gtar' => 'application/x-gtar', + 'php3' => 'application/x-httpd-php', + 'php4' => 'application/x-httpd-php', + 'php' => 'application/x-httpd-php', + 'phtml' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'tar' => 'application/x-tar', + 'tgz' => 'application/x-tar', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'zip' => 'application/zip', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'm4a' => 'audio/mp4', + 'mpga' => 'audio/mpeg', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'ra' => 'audio/x-realaudio', + 'wav' => 'audio/x-wav', + 'mka' => 'audio/x-matroska', + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'jpeg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'png' => 'image/png', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'webp' => 'image/webp', + 'heif' => 'image/heif', + 'heifs' => 'image/heif-sequence', + 'heic' => 'image/heic', + 'heics' => 'image/heic-sequence', + 'eml' => 'message/rfc822', + 'css' => 'text/css', + 'html' => 'text/html', + 'htm' => 'text/html', + 'shtml' => 'text/html', + 'log' => 'text/plain', + 'text' => 'text/plain', + 'txt' => 'text/plain', + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'vcf' => 'text/vcard', + 'vcard' => 'text/vcard', + 'ics' => 'text/calendar', + 'xml' => 'text/xml', + 'xsl' => 'text/xml', + 'wmv' => 'video/x-ms-wmv', + 'mpeg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mp4' => 'video/mp4', + 'm4v' => 'video/mp4', + 'mov' => 'video/quicktime', + 'qt' => 'video/quicktime', + 'rv' => 'video/vnd.rn-realvideo', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie', + 'webm' => 'video/webm', + 'mkv' => 'video/x-matroska', + ]; + $ext = strtolower($ext); + if (array_key_exists($ext, $mimes)) { + return $mimes[$ext]; + } + + return 'application/octet-stream'; + } + + /** + * Map a file name to a MIME type. + * Defaults to 'application/octet-stream', i.e.. arbitrary binary data. + * + * @param string $filename A file name or full path, does not need to exist as a file + * + * @return string + */ + public static function filenameToType($filename) + { + // In case the path is a URL, strip any query string before getting extension + $qpos = strpos($filename, '?'); + if (false !== $qpos) { + $filename = substr($filename, 0, $qpos); + } + $ext = static::mb_pathinfo($filename, PATHINFO_EXTENSION); + + return static::_mime_types($ext); + } + + /** + * Multi-byte-safe pathinfo replacement. + * Drop-in replacement for pathinfo(), but multibyte- and cross-platform-safe. + * + * @see http://www.php.net/manual/en/function.pathinfo.php#107461 + * + * @param string $path A filename or path, does not need to exist as a file + * @param int|string $options Either a PATHINFO_* constant, + * or a string name to return only the specified piece + * + * @return string|array + */ + public static function mb_pathinfo($path, $options = null) + { + $ret = ['dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '']; + $pathinfo = []; + if (preg_match('#^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^.\\\\/]+?)|))[\\\\/.]*$#m', $path, $pathinfo)) { + if (array_key_exists(1, $pathinfo)) { + $ret['dirname'] = $pathinfo[1]; + } + if (array_key_exists(2, $pathinfo)) { + $ret['basename'] = $pathinfo[2]; + } + if (array_key_exists(5, $pathinfo)) { + $ret['extension'] = $pathinfo[5]; + } + if (array_key_exists(3, $pathinfo)) { + $ret['filename'] = $pathinfo[3]; + } + } + switch ($options) { + case PATHINFO_DIRNAME: + case 'dirname': + return $ret['dirname']; + case PATHINFO_BASENAME: + case 'basename': + return $ret['basename']; + case PATHINFO_EXTENSION: + case 'extension': + return $ret['extension']; + case PATHINFO_FILENAME: + case 'filename': + return $ret['filename']; + default: + return $ret; + } + } + + /** + * Set or reset instance properties. + * You should avoid this function - it's more verbose, less efficient, more error-prone and + * harder to debug than setting properties directly. + * Usage Example: + * `$mail->set('SMTPSecure', static::ENCRYPTION_STARTTLS);` + * is the same as: + * `$mail->SMTPSecure = static::ENCRYPTION_STARTTLS;`. + * + * @param string $name The property name to set + * @param mixed $value The value to set the property to + * + * @return bool + */ + public function set($name, $value = '') + { + if (property_exists($this, $name)) { + $this->$name = $value; + + return true; + } + $this->setError($this->lang('variable_set') . $name); + + return false; + } + + /** + * Strip newlines to prevent header injection. + * + * @param string $str + * + * @return string + */ + public function secureHeader($str) + { + return trim(str_replace(["\r", "\n"], '', $str)); + } + + /** + * Normalize line breaks in a string. + * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format. + * Defaults to CRLF (for message bodies) and preserves consecutive breaks. + * + * @param string $text + * @param string $breaktype What kind of line break to use; defaults to static::$LE + * + * @return string + */ + public static function normalizeBreaks($text, $breaktype = null) + { + if (null === $breaktype) { + $breaktype = static::$LE; + } + // Normalise to \n + $text = str_replace(["\r\n", "\r"], "\n", $text); + // Now convert LE as needed + if ("\n" !== $breaktype) { + $text = str_replace("\n", $breaktype, $text); + } + + return $text; + } + + /** + * Return the current line break format string. + * + * @return string + */ + public static function getLE() + { + return static::$LE; + } + + /** + * Set the line break format string, e.g. "\r\n". + * + * @param string $le + */ + protected static function setLE($le) + { + static::$LE = $le; + } + + /** + * Set the public and private key files and password for S/MIME signing. + * + * @param string $cert_filename + * @param string $key_filename + * @param string $key_pass Password for private key + * @param string $extracerts_filename Optional path to chain certificate + */ + public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '') + { + $this->sign_cert_file = $cert_filename; + $this->sign_key_file = $key_filename; + $this->sign_key_pass = $key_pass; + $this->sign_extracerts_file = $extracerts_filename; + } + + /** + * Quoted-Printable-encode a DKIM header. + * + * @param string $txt + * + * @return string + */ + public function DKIM_QP($txt) + { + $line = ''; + $len = strlen($txt); + for ($i = 0; $i < $len; ++$i) { + $ord = ord($txt[$i]); + if (((0x21 <= $ord) and ($ord <= 0x3A)) or $ord == 0x3C or ((0x3E <= $ord) and ($ord <= 0x7E))) { + $line .= $txt[$i]; + } else { + $line .= '=' . sprintf('%02X', $ord); + } + } + + return $line; + } + + /** + * Generate a DKIM signature. + * + * @param string $signHeader + * + * @throws Exception + * + * @return string The DKIM signature value + */ + public function DKIM_Sign($signHeader) + { + if (!defined('PKCS7_TEXT')) { + if ($this->exceptions) { + throw new Exception($this->lang('extension_missing') . 'openssl'); + } + + return ''; + } + $privKeyStr = !empty($this->DKIM_private_string) ? + $this->DKIM_private_string : + file_get_contents($this->DKIM_private); + if ('' != $this->DKIM_passphrase) { + $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); + } else { + $privKey = openssl_pkey_get_private($privKeyStr); + } + if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { + openssl_pkey_free($privKey); + + return base64_encode($signature); + } + openssl_pkey_free($privKey); + + return ''; + } + + /** + * Generate a DKIM canonicalization header. + * Uses the 'relaxed' algorithm from RFC6376 section 3.4.2. + * Canonicalized headers should *always* use CRLF, regardless of mailer setting. + * + * @see https://tools.ietf.org/html/rfc6376#section-3.4.2 + * + * @param string $signHeader Header + * + * @return string + */ + public function DKIM_HeaderC($signHeader) + { + //Note PCRE \s is too broad a definition of whitespace; RFC5322 defines it as `[ \t]` + //@see https://tools.ietf.org/html/rfc5322#section-2.2 + //That means this may break if you do something daft like put vertical tabs in your headers. + //Unfold header lines + $signHeader = preg_replace('/\r\n[ \t]+/m', '', $signHeader); + //Collapse internal whitespace to a single space +// $signHeader = preg_replace('/[ \t]+/', ' ', $signHeader); + //Break headers out into an array + $lines = explode("\r\n", $signHeader); + foreach ($lines as $key => $line) { + //If the header is missing a :, skip it as it's invalid + //This is likely to happen because the explode() above will also split + //on the trailing LE, leaving an empty line + if (strpos($line, ':') === false) { + continue; + } + list($heading, $value) = explode(':', $line, 2); + //Lower-case header name + $heading = strtolower($heading); + //Collapse white space within the value, also convert WSP to space + $value = preg_replace('/[ \t]+/', ' ', $value); + //RFC6376 is slightly unclear here - it says to delete space at the *end* of each value + //But then says to delete space before and after the colon. + //Net result is the same as trimming both ends of the value. + //By elimination, the same applies to the field name + $lines[$key] = trim($heading, " \t") . ':' . trim($value, " \t"); + } + + return implode("\r\n", $lines); + } + + /** + * Generate a DKIM canonicalization body. + * Uses the 'simple' algorithm from RFC6376 section 3.4.3. + * Canonicalized bodies should *always* use CRLF, regardless of mailer setting. + * + * @see https://tools.ietf.org/html/rfc6376#section-3.4.3 + * + * @param string $body Message Body + * + * @return string + */ + public function DKIM_BodyC($body) + { + if (empty($body)) { + return "\r\n"; + } + // Normalize line endings to CRLF + $body = static::normalizeBreaks($body, "\r\n"); + + //Reduce multiple trailing line breaks to a single one + return rtrim($body, "\r\n") . "\r\n"; + } + + /** + * Create the DKIM header and body in a new message header. + * + * @param string $headers_line Header lines + * @param string $subject Subject + * @param string $body Body + * + * @return string + */ + public function DKIM_Add($headers_line, $subject, $body) + { + $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms + $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body + $DKIMquery = 'dns/txt'; // Query method + $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) + $subject_header = "Subject: $subject"; + $headers = explode(static::$LE, $headers_line); + $from_header = ''; + $to_header = ''; + $date_header = ''; + $current = ''; + $copiedHeaderFields = ''; + $foundExtraHeaders = []; + $extraHeaderKeys = ''; + $extraHeaderValues = ''; + $extraCopyHeaderFields = ''; + foreach ($headers as $header) { + if (strpos($header, 'From:') === 0) { + $from_header = $header; + $current = 'from_header'; + } elseif (strpos($header, 'To:') === 0) { + $to_header = $header; + $current = 'to_header'; + } elseif (strpos($header, 'Date:') === 0) { + $date_header = $header; + $current = 'date_header'; + } elseif (!empty($this->DKIM_extraHeaders)) { + foreach ($this->DKIM_extraHeaders as $extraHeader) { + if (strpos($header, $extraHeader . ':') === 0) { + $headerValue = $header; + foreach ($this->CustomHeader as $customHeader) { + if ($customHeader[0] === $extraHeader) { + $headerValue = trim($customHeader[0]) . + ': ' . + $this->encodeHeader(trim($customHeader[1])); + break; + } + } + $foundExtraHeaders[$extraHeader] = $headerValue; + $current = ''; + break; + } + } + } else { + if (!empty($$current) and strpos($header, ' =?') === 0) { + $$current .= $header; + } else { + $current = ''; + } + } + } + foreach ($foundExtraHeaders as $key => $value) { + $extraHeaderKeys .= ':' . $key; + $extraHeaderValues .= $value . "\r\n"; + if ($this->DKIM_copyHeaderFields) { + $extraCopyHeaderFields .= ' |' . str_replace('|', '=7C', $this->DKIM_QP($value)) . ";\r\n"; + } + } + if ($this->DKIM_copyHeaderFields) { + $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); + $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); + $date = str_replace('|', '=7C', $this->DKIM_QP($date_header)); + $subject = str_replace('|', '=7C', $this->DKIM_QP($subject_header)); + $copiedHeaderFields = " z=$from\r\n" . + " |$to\r\n" . + " |$date\r\n" . + " |$subject;\r\n" . + $extraCopyHeaderFields; + } + $body = $this->DKIM_BodyC($body); + $DKIMlen = strlen($body); // Length of body + $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body + if ('' == $this->DKIM_identity) { + $ident = ''; + } else { + $ident = ' i=' . $this->DKIM_identity . ';'; + } + $dkimhdrs = 'DKIM-Signature: v=1; a=' . + $DKIMsignatureType . '; q=' . + $DKIMquery . '; l=' . + $DKIMlen . '; s=' . + $this->DKIM_selector . + ";\r\n" . + ' t=' . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" . + ' h=From:To:Date:Subject' . $extraHeaderKeys . ";\r\n" . + ' d=' . $this->DKIM_domain . ';' . $ident . "\r\n" . + $copiedHeaderFields . + ' bh=' . $DKIMb64 . ";\r\n" . + ' b='; + $toSign = $this->DKIM_HeaderC( + $from_header . "\r\n" . + $to_header . "\r\n" . + $date_header . "\r\n" . + $subject_header . "\r\n" . + $extraHeaderValues . + $dkimhdrs + ); + $signed = $this->DKIM_Sign($toSign); + + return static::normalizeBreaks($dkimhdrs . $signed) . static::$LE; + } + + /** + * Detect if a string contains a line longer than the maximum line length + * allowed by RFC 2822 section 2.1.1. + * + * @param string $str + * + * @return bool + */ + public static function hasLineLongerThanMax($str) + { + return (bool) preg_match('/^(.{' . (self::MAX_LINE_LENGTH + strlen(static::$LE)) . ',})/m', $str); + } + + /** + * Allows for public read access to 'to' property. + * Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * + * @return array + */ + public function getToAddresses() + { + return $this->to; + } + + /** + * Allows for public read access to 'cc' property. + * Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * + * @return array + */ + public function getCcAddresses() + { + return $this->cc; + } + + /** + * Allows for public read access to 'bcc' property. + * Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * + * @return array + */ + public function getBccAddresses() + { + return $this->bcc; + } + + /** + * Allows for public read access to 'ReplyTo' property. + * Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * + * @return array + */ + public function getReplyToAddresses() + { + return $this->ReplyTo; + } + + /** + * Allows for public read access to 'all_recipients' property. + * Before the send() call, queued addresses (i.e. with IDN) are not yet included. + * + * @return array + */ + public function getAllRecipientAddresses() + { + return $this->all_recipients; + } + + /** + * Perform a callback. + * + * @param bool $isSent + * @param array $to + * @param array $cc + * @param array $bcc + * @param string $subject + * @param string $body + * @param string $from + * @param array $extra + */ + protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from, $extra) + { + if (!empty($this->action_function) and is_callable($this->action_function)) { + call_user_func($this->action_function, $isSent, $to, $cc, $bcc, $subject, $body, $from, $extra); + } + } + + /** + * Get the OAuth instance. + * + * @return OAuth + */ + public function getOAuth() + { + return $this->oauth; + } + + /** + * Set an OAuth instance. + * + * @param OAuth $oauth + */ + public function setOAuth(OAuth $oauth) + { + $this->oauth = $oauth; + } +} diff --git a/vendor/phpmailer/phpmailer/src/POP3.php b/vendor/phpmailer/phpmailer/src/POP3.php new file mode 100644 index 0000000000..e6ad6310af --- /dev/null +++ b/vendor/phpmailer/phpmailer/src/POP3.php @@ -0,0 +1,419 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2019 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +namespace PHPMailer\PHPMailer; + +/** + * PHPMailer POP-Before-SMTP Authentication Class. + * Specifically for PHPMailer to use for RFC1939 POP-before-SMTP authentication. + * 1) This class does not support APOP authentication. + * 2) Opening and closing lots of POP3 connections can be quite slow. If you need + * to send a batch of emails then just perform the authentication once at the start, + * and then loop through your mail sending script. Providing this process doesn't + * take longer than the verification period lasts on your POP3 server, you should be fine. + * 3) This is really ancient technology; you should only need to use it to talk to very old systems. + * 4) This POP3 class is deliberately lightweight and incomplete, implementing just + * enough to do authentication. + * If you want a more complete class there are other POP3 classes for PHP available. + * + * @author Richard Davey (original author) + * @author Marcus Bointon (Synchro/coolbru) + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + */ +class POP3 +{ + /** + * The POP3 PHPMailer Version number. + * + * @var string + */ + const VERSION = '6.1.1'; + + /** + * Default POP3 port number. + * + * @var int + */ + const DEFAULT_PORT = 110; + + /** + * Default timeout in seconds. + * + * @var int + */ + const DEFAULT_TIMEOUT = 30; + + /** + * Debug display level. + * Options: 0 = no, 1+ = yes. + * + * @var int + */ + public $do_debug = 0; + + /** + * POP3 mail server hostname. + * + * @var string + */ + public $host; + + /** + * POP3 port number. + * + * @var int + */ + public $port; + + /** + * POP3 Timeout Value in seconds. + * + * @var int + */ + public $tval; + + /** + * POP3 username. + * + * @var string + */ + public $username; + + /** + * POP3 password. + * + * @var string + */ + public $password; + + /** + * Resource handle for the POP3 connection socket. + * + * @var resource + */ + protected $pop_conn; + + /** + * Are we connected? + * + * @var bool + */ + protected $connected = false; + + /** + * Error container. + * + * @var array + */ + protected $errors = []; + + /** + * Line break constant. + */ + const LE = "\r\n"; + + /** + * Simple static wrapper for all-in-one POP before SMTP. + * + * @param string $host The hostname to connect to + * @param int|bool $port The port number to connect to + * @param int|bool $timeout The timeout value + * @param string $username + * @param string $password + * @param int $debug_level + * + * @return bool + */ + public static function popBeforeSmtp( + $host, + $port = false, + $timeout = false, + $username = '', + $password = '', + $debug_level = 0 + ) { + $pop = new self(); + + return $pop->authorise($host, $port, $timeout, $username, $password, $debug_level); + } + + /** + * Authenticate with a POP3 server. + * A connect, login, disconnect sequence + * appropriate for POP-before SMTP authorisation. + * + * @param string $host The hostname to connect to + * @param int|bool $port The port number to connect to + * @param int|bool $timeout The timeout value + * @param string $username + * @param string $password + * @param int $debug_level + * + * @return bool + */ + public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0) + { + $this->host = $host; + // If no port value provided, use default + if (false === $port) { + $this->port = static::DEFAULT_PORT; + } else { + $this->port = (int) $port; + } + // If no timeout value provided, use default + if (false === $timeout) { + $this->tval = static::DEFAULT_TIMEOUT; + } else { + $this->tval = (int) $timeout; + } + $this->do_debug = $debug_level; + $this->username = $username; + $this->password = $password; + // Reset the error log + $this->errors = []; + // connect + $result = $this->connect($this->host, $this->port, $this->tval); + if ($result) { + $login_result = $this->login($this->username, $this->password); + if ($login_result) { + $this->disconnect(); + + return true; + } + } + // We need to disconnect regardless of whether the login succeeded + $this->disconnect(); + + return false; + } + + /** + * Connect to a POP3 server. + * + * @param string $host + * @param int|bool $port + * @param int $tval + * + * @return bool + */ + public function connect($host, $port = false, $tval = 30) + { + // Are we already connected? + if ($this->connected) { + return true; + } + + //On Windows this will raise a PHP Warning error if the hostname doesn't exist. + //Rather than suppress it with @fsockopen, capture it cleanly instead + set_error_handler([$this, 'catchWarning']); + + if (false === $port) { + $port = static::DEFAULT_PORT; + } + + // connect to the POP3 server + $this->pop_conn = fsockopen( + $host, // POP3 Host + $port, // Port # + $errno, // Error Number + $errstr, // Error Message + $tval + ); // Timeout (seconds) + // Restore the error handler + restore_error_handler(); + + // Did we connect? + if (false === $this->pop_conn) { + // It would appear not... + $this->setError( + "Failed to connect to server $host on port $port. errno: $errno; errstr: $errstr" + ); + + return false; + } + + // Increase the stream time-out + stream_set_timeout($this->pop_conn, $tval, 0); + + // Get the POP3 server response + $pop3_response = $this->getResponse(); + // Check for the +OK + if ($this->checkResponse($pop3_response)) { + // The connection is established and the POP3 server is talking + $this->connected = true; + + return true; + } + + return false; + } + + /** + * Log in to the POP3 server. + * Does not support APOP (RFC 2828, 4949). + * + * @param string $username + * @param string $password + * + * @return bool + */ + public function login($username = '', $password = '') + { + if (!$this->connected) { + $this->setError('Not connected to POP3 server'); + } + if (empty($username)) { + $username = $this->username; + } + if (empty($password)) { + $password = $this->password; + } + + // Send the Username + $this->sendString("USER $username" . static::LE); + $pop3_response = $this->getResponse(); + if ($this->checkResponse($pop3_response)) { + // Send the Password + $this->sendString("PASS $password" . static::LE); + $pop3_response = $this->getResponse(); + if ($this->checkResponse($pop3_response)) { + return true; + } + } + + return false; + } + + /** + * Disconnect from the POP3 server. + */ + public function disconnect() + { + $this->sendString('QUIT'); + //The QUIT command may cause the daemon to exit, which will kill our connection + //So ignore errors here + try { + @fclose($this->pop_conn); + } catch (Exception $e) { + //Do nothing + } + } + + /** + * Get a response from the POP3 server. + * + * @param int $size The maximum number of bytes to retrieve + * + * @return string + */ + protected function getResponse($size = 128) + { + $response = fgets($this->pop_conn, $size); + if ($this->do_debug >= 1) { + echo 'Server -> Client: ', $response; + } + + return $response; + } + + /** + * Send raw data to the POP3 server. + * + * @param string $string + * + * @return int + */ + protected function sendString($string) + { + if ($this->pop_conn) { + if ($this->do_debug >= 2) { //Show client messages when debug >= 2 + echo 'Client -> Server: ', $string; + } + + return fwrite($this->pop_conn, $string, strlen($string)); + } + + return 0; + } + + /** + * Checks the POP3 server response. + * Looks for for +OK or -ERR. + * + * @param string $string + * + * @return bool + */ + protected function checkResponse($string) + { + if (substr($string, 0, 3) !== '+OK') { + $this->setError("Server reported an error: $string"); + + return false; + } + + return true; + } + + /** + * Add an error to the internal error store. + * Also display debug output if it's enabled. + * + * @param string $error + */ + protected function setError($error) + { + $this->errors[] = $error; + if ($this->do_debug >= 1) { + echo '
';
+            foreach ($this->errors as $e) {
+                print_r($e);
+            }
+            echo '
'; + } + } + + /** + * Get an array of error messages, if any. + * + * @return array + */ + public function getErrors() + { + return $this->errors; + } + + /** + * POP3 connection error handler. + * + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + */ + protected function catchWarning($errno, $errstr, $errfile, $errline) + { + $this->setError( + 'Connecting to the POP3 server raised a PHP warning:' . + "errno: $errno errstr: $errstr; errfile: $errfile; errline: $errline" + ); + } +} diff --git a/vendor/phpmailer/phpmailer/src/SMTP.php b/vendor/phpmailer/phpmailer/src/SMTP.php new file mode 100644 index 0000000000..77df57ce97 --- /dev/null +++ b/vendor/phpmailer/phpmailer/src/SMTP.php @@ -0,0 +1,1357 @@ + + * @author Jim Jagielski (jimjag) + * @author Andy Prevost (codeworxtech) + * @author Brent R. Matzelle (original founder) + * @copyright 2012 - 2019 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +namespace PHPMailer\PHPMailer; + +/** + * PHPMailer RFC821 SMTP email transport class. + * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server. + * + * @author Chris Ryan + * @author Marcus Bointon + */ +class SMTP +{ + /** + * The PHPMailer SMTP version number. + * + * @var string + */ + const VERSION = '6.1.1'; + + /** + * SMTP line break constant. + * + * @var string + */ + const LE = "\r\n"; + + /** + * The SMTP port to use if one is not specified. + * + * @var int + */ + const DEFAULT_PORT = 25; + + /** + * The maximum line length allowed by RFC 2822 section 2.1.1. + * + * @var int + */ + const MAX_LINE_LENGTH = 998; + + /** + * Debug level for no output. + * + * @var int + */ + const DEBUG_OFF = 0; + + /** + * Debug level to show client -> server messages. + * + * @var int + */ + const DEBUG_CLIENT = 1; + + /** + * Debug level to show client -> server and server -> client messages. + * + * @var int + */ + const DEBUG_SERVER = 2; + + /** + * Debug level to show connection status, client -> server and server -> client messages. + * + * @var int + */ + const DEBUG_CONNECTION = 3; + + /** + * Debug level to show all messages. + * + * @var int + */ + const DEBUG_LOWLEVEL = 4; + + /** + * Debug output level. + * Options: + * * self::DEBUG_OFF (`0`) No debug output, default + * * self::DEBUG_CLIENT (`1`) Client commands + * * self::DEBUG_SERVER (`2`) Client commands and server responses + * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status + * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages. + * + * @var int + */ + public $do_debug = self::DEBUG_OFF; + + /** + * How to handle debug output. + * Options: + * * `echo` Output plain-text as-is, appropriate for CLI + * * `html` Output escaped, line breaks converted to `
`, appropriate for browser output + * * `error_log` Output to error log as configured in php.ini + * Alternatively, you can provide a callable expecting two params: a message string and the debug level: + * + * ```php + * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; + * ``` + * + * Alternatively, you can pass in an instance of a PSR-3 compatible logger, though only `debug` + * level output is used: + * + * ```php + * $mail->Debugoutput = new myPsr3Logger; + * ``` + * + * @var string|callable|\Psr\Log\LoggerInterface + */ + public $Debugoutput = 'echo'; + + /** + * Whether to use VERP. + * + * @see http://en.wikipedia.org/wiki/Variable_envelope_return_path + * @see http://www.postfix.org/VERP_README.html Info on VERP + * + * @var bool + */ + public $do_verp = false; + + /** + * The timeout value for connection, in seconds. + * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2. + * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure. + * + * @see http://tools.ietf.org/html/rfc2821#section-4.5.3.2 + * + * @var int + */ + public $Timeout = 300; + + /** + * How long to wait for commands to complete, in seconds. + * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2. + * + * @var int + */ + public $Timelimit = 300; + + /** + * Patterns to extract an SMTP transaction id from reply to a DATA command. + * The first capture group in each regex will be used as the ID. + * MS ESMTP returns the message ID, which may not be correct for internal tracking. + * + * @var string[] + */ + protected $smtp_transaction_id_patterns = [ + 'exim' => '/[\d]{3} OK id=(.*)/', + 'sendmail' => '/[\d]{3} 2.0.0 (.*) Message/', + 'postfix' => '/[\d]{3} 2.0.0 Ok: queued as (.*)/', + 'Microsoft_ESMTP' => '/[0-9]{3} 2.[\d].0 (.*)@(?:.*) Queued mail for delivery/', + 'Amazon_SES' => '/[\d]{3} Ok (.*)/', + 'SendGrid' => '/[\d]{3} Ok: queued as (.*)/', + 'CampaignMonitor' => '/[\d]{3} 2.0.0 OK:([a-zA-Z\d]{48})/', + ]; + + /** + * The last transaction ID issued in response to a DATA command, + * if one was detected. + * + * @var string|bool|null + */ + protected $last_smtp_transaction_id; + + /** + * The socket for the server connection. + * + * @var ?resource + */ + protected $smtp_conn; + + /** + * Error information, if any, for the last SMTP command. + * + * @var array + */ + protected $error = [ + 'error' => '', + 'detail' => '', + 'smtp_code' => '', + 'smtp_code_ex' => '', + ]; + + /** + * The reply the server sent to us for HELO. + * If null, no HELO string has yet been received. + * + * @var string|null + */ + protected $helo_rply = null; + + /** + * The set of SMTP extensions sent in reply to EHLO command. + * Indexes of the array are extension names. + * Value at index 'HELO' or 'EHLO' (according to command that was sent) + * represents the server name. In case of HELO it is the only element of the array. + * Other values can be boolean TRUE or an array containing extension options. + * If null, no HELO/EHLO string has yet been received. + * + * @var array|null + */ + protected $server_caps = null; + + /** + * The most recent reply received from the server. + * + * @var string + */ + protected $last_reply = ''; + + /** + * Output debugging info via a user-selected method. + * + * @param string $str Debug string to output + * @param int $level The debug level of this message; see DEBUG_* constants + * + * @see SMTP::$Debugoutput + * @see SMTP::$do_debug + */ + protected function edebug($str, $level = 0) + { + if ($level > $this->do_debug) { + return; + } + //Is this a PSR-3 logger? + if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) { + $this->Debugoutput->debug($str); + + return; + } + //Avoid clash with built-in function names + if (!in_array($this->Debugoutput, ['error_log', 'html', 'echo']) and is_callable($this->Debugoutput)) { + call_user_func($this->Debugoutput, $str, $level); + + return; + } + switch ($this->Debugoutput) { + case 'error_log': + //Don't output, just log + error_log($str); + break; + case 'html': + //Cleans up output a bit for a better looking, HTML-safe output + echo gmdate('Y-m-d H:i:s'), ' ', htmlentities( + preg_replace('/[\r\n]+/', '', $str), + ENT_QUOTES, + 'UTF-8' + ), "
\n"; + break; + case 'echo': + default: + //Normalize line breaks + $str = preg_replace('/\r\n|\r/ms', "\n", $str); + echo gmdate('Y-m-d H:i:s'), + "\t", + //Trim trailing space + trim( + //Indent for readability, except for trailing break + str_replace( + "\n", + "\n \t ", + trim($str) + ) + ), + "\n"; + } + } + + /** + * Connect to an SMTP server. + * + * @param string $host SMTP server IP or host name + * @param int $port The port number to connect to + * @param int $timeout How long to wait for the connection to open + * @param array $options An array of options for stream_context_create() + * + * @return bool + */ + public function connect($host, $port = null, $timeout = 30, $options = []) + { + static $streamok; + //This is enabled by default since 5.0.0 but some providers disable it + //Check this once and cache the result + if (null === $streamok) { + $streamok = function_exists('stream_socket_client'); + } + // Clear errors to avoid confusion + $this->setError(''); + // Make sure we are __not__ connected + if ($this->connected()) { + // Already connected, generate error + $this->setError('Already connected to a server'); + + return false; + } + if (empty($port)) { + $port = self::DEFAULT_PORT; + } + // Connect to the SMTP server + $this->edebug( + "Connection: opening to $host:$port, timeout=$timeout, options=" . + (count($options) > 0 ? var_export($options, true) : 'array()'), + self::DEBUG_CONNECTION + ); + $errno = 0; + $errstr = ''; + if ($streamok) { + $socket_context = stream_context_create($options); + set_error_handler([$this, 'errorHandler']); + $this->smtp_conn = stream_socket_client( + $host . ':' . $port, + $errno, + $errstr, + $timeout, + STREAM_CLIENT_CONNECT, + $socket_context + ); + restore_error_handler(); + } else { + //Fall back to fsockopen which should work in more places, but is missing some features + $this->edebug( + 'Connection: stream_socket_client not available, falling back to fsockopen', + self::DEBUG_CONNECTION + ); + set_error_handler([$this, 'errorHandler']); + $this->smtp_conn = fsockopen( + $host, + $port, + $errno, + $errstr, + $timeout + ); + restore_error_handler(); + } + // Verify we connected properly + if (!is_resource($this->smtp_conn)) { + $this->setError( + 'Failed to connect to server', + '', + (string) $errno, + (string) $errstr + ); + $this->edebug( + 'SMTP ERROR: ' . $this->error['error'] + . ": $errstr ($errno)", + self::DEBUG_CLIENT + ); + + return false; + } + $this->edebug('Connection: opened', self::DEBUG_CONNECTION); + // SMTP server can take longer to respond, give longer timeout for first read + // Windows does not have support for this timeout function + if (substr(PHP_OS, 0, 3) != 'WIN') { + $max = ini_get('max_execution_time'); + // Don't bother if unlimited + if (0 != $max and $timeout > $max) { + @set_time_limit($timeout); + } + stream_set_timeout($this->smtp_conn, $timeout, 0); + } + // Get any announcement + $announce = $this->get_lines(); + $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER); + + return true; + } + + /** + * Initiate a TLS (encrypted) session. + * + * @return bool + */ + public function startTLS() + { + if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { + return false; + } + + //Allow the best TLS version(s) we can + $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; + + //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT + //so add them back in manually if we can + if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { + $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; + $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; + } + + // Begin encrypted connection + set_error_handler([$this, 'errorHandler']); + $crypto_ok = stream_socket_enable_crypto( + $this->smtp_conn, + true, + $crypto_method + ); + restore_error_handler(); + + return (bool) $crypto_ok; + } + + /** + * Perform SMTP authentication. + * Must be run after hello(). + * + * @see hello() + * + * @param string $username The user name + * @param string $password The password + * @param string $authtype The auth type (CRAM-MD5, PLAIN, LOGIN, XOAUTH2) + * @param OAuth $OAuth An optional OAuth instance for XOAUTH2 authentication + * + * @return bool True if successfully authenticated + */ + public function authenticate( + $username, + $password, + $authtype = null, + $OAuth = null + ) { + if (!$this->server_caps) { + $this->setError('Authentication is not allowed before HELO/EHLO'); + + return false; + } + + if (array_key_exists('EHLO', $this->server_caps)) { + // SMTP extensions are available; try to find a proper authentication method + if (!array_key_exists('AUTH', $this->server_caps)) { + $this->setError('Authentication is not allowed at this stage'); + // 'at this stage' means that auth may be allowed after the stage changes + // e.g. after STARTTLS + + return false; + } + + $this->edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNSPECIFIED'), self::DEBUG_LOWLEVEL); + $this->edebug( + 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']), + self::DEBUG_LOWLEVEL + ); + + //If we have requested a specific auth type, check the server supports it before trying others + if (null !== $authtype and !in_array($authtype, $this->server_caps['AUTH'])) { + $this->edebug('Requested auth method not available: ' . $authtype, self::DEBUG_LOWLEVEL); + $authtype = null; + } + + if (empty($authtype)) { + //If no auth mechanism is specified, attempt to use these, in this order + //Try CRAM-MD5 first as it's more secure than the others + foreach (['CRAM-MD5', 'LOGIN', 'PLAIN', 'XOAUTH2'] as $method) { + if (in_array($method, $this->server_caps['AUTH'])) { + $authtype = $method; + break; + } + } + if (empty($authtype)) { + $this->setError('No supported authentication methods found'); + + return false; + } + self::edebug('Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL); + } + + if (!in_array($authtype, $this->server_caps['AUTH'])) { + $this->setError("The requested authentication method \"$authtype\" is not supported by the server"); + + return false; + } + } elseif (empty($authtype)) { + $authtype = 'LOGIN'; + } + switch ($authtype) { + case 'PLAIN': + // Start authentication + if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { + return false; + } + // Send encoded username and password + if (!$this->sendCommand( + 'User & Password', + base64_encode("\0" . $username . "\0" . $password), + 235 + ) + ) { + return false; + } + break; + case 'LOGIN': + // Start authentication + if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) { + return false; + } + if (!$this->sendCommand('Username', base64_encode($username), 334)) { + return false; + } + if (!$this->sendCommand('Password', base64_encode($password), 235)) { + return false; + } + break; + case 'CRAM-MD5': + // Start authentication + if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { + return false; + } + // Get the challenge + $challenge = base64_decode(substr($this->last_reply, 4)); + + // Build the response + $response = $username . ' ' . $this->hmac($challenge, $password); + + // send encoded credentials + return $this->sendCommand('Username', base64_encode($response), 235); + case 'XOAUTH2': + //The OAuth instance must be set up prior to requesting auth. + if (null === $OAuth) { + return false; + } + $oauth = $OAuth->getOauth64(); + + // Start authentication + if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { + return false; + } + break; + default: + $this->setError("Authentication method \"$authtype\" is not supported"); + + return false; + } + + return true; + } + + /** + * Calculate an MD5 HMAC hash. + * Works like hash_hmac('md5', $data, $key) + * in case that function is not available. + * + * @param string $data The data to hash + * @param string $key The key to hash with + * + * @return string + */ + protected function hmac($data, $key) + { + if (function_exists('hash_hmac')) { + return hash_hmac('md5', $data, $key); + } + + // The following borrowed from + // http://php.net/manual/en/function.mhash.php#27225 + + // RFC 2104 HMAC implementation for php. + // Creates an md5 HMAC. + // Eliminates the need to install mhash to compute a HMAC + // by Lance Rushing + + $bytelen = 64; // byte length for md5 + if (strlen($key) > $bytelen) { + $key = pack('H*', md5($key)); + } + $key = str_pad($key, $bytelen, chr(0x00)); + $ipad = str_pad('', $bytelen, chr(0x36)); + $opad = str_pad('', $bytelen, chr(0x5c)); + $k_ipad = $key ^ $ipad; + $k_opad = $key ^ $opad; + + return md5($k_opad . pack('H*', md5($k_ipad . $data))); + } + + /** + * Check connection state. + * + * @return bool True if connected + */ + public function connected() + { + if (is_resource($this->smtp_conn)) { + $sock_status = stream_get_meta_data($this->smtp_conn); + if ($sock_status['eof']) { + // The socket is valid but we are not connected + $this->edebug( + 'SMTP NOTICE: EOF caught while checking if connected', + self::DEBUG_CLIENT + ); + $this->close(); + + return false; + } + + return true; // everything looks good + } + + return false; + } + + /** + * Close the socket and clean up the state of the class. + * Don't use this function without first trying to use QUIT. + * + * @see quit() + */ + public function close() + { + $this->setError(''); + $this->server_caps = null; + $this->helo_rply = null; + if (is_resource($this->smtp_conn)) { + // close the connection and cleanup + fclose($this->smtp_conn); + $this->smtp_conn = null; //Makes for cleaner serialization + $this->edebug('Connection: closed', self::DEBUG_CONNECTION); + } + } + + /** + * Send an SMTP DATA command. + * Issues a data command and sends the msg_data to the server, + * finializing the mail transaction. $msg_data is the message + * that is to be send with the headers. Each header needs to be + * on a single line followed by a with the message headers + * and the message body being separated by an additional . + * Implements RFC 821: DATA . + * + * @param string $msg_data Message data to send + * + * @return bool + */ + public function data($msg_data) + { + //This will use the standard timelimit + if (!$this->sendCommand('DATA', 'DATA', 354)) { + return false; + } + + /* The server is ready to accept data! + * According to rfc821 we should not send more than 1000 characters on a single line (including the LE) + * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into + * smaller lines to fit within the limit. + * We will also look for lines that start with a '.' and prepend an additional '.'. + * NOTE: this does not count towards line-length limit. + */ + + // Normalize line breaks before exploding + $lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data)); + + /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field + * of the first line (':' separated) does not contain a space then it _should_ be a header and we will + * process all lines before a blank line as headers. + */ + + $field = substr($lines[0], 0, strpos($lines[0], ':')); + $in_headers = false; + if (!empty($field) and strpos($field, ' ') === false) { + $in_headers = true; + } + + foreach ($lines as $line) { + $lines_out = []; + if ($in_headers and $line == '') { + $in_headers = false; + } + //Break this line up into several smaller lines if it's too long + //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len), + while (isset($line[self::MAX_LINE_LENGTH])) { + //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on + //so as to avoid breaking in the middle of a word + $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' '); + //Deliberately matches both false and 0 + if (!$pos) { + //No nice break found, add a hard break + $pos = self::MAX_LINE_LENGTH - 1; + $lines_out[] = substr($line, 0, $pos); + $line = substr($line, $pos); + } else { + //Break at the found point + $lines_out[] = substr($line, 0, $pos); + //Move along by the amount we dealt with + $line = substr($line, $pos + 1); + } + //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1 + if ($in_headers) { + $line = "\t" . $line; + } + } + $lines_out[] = $line; + + //Send the lines to the server + foreach ($lines_out as $line_out) { + //RFC2821 section 4.5.2 + if (!empty($line_out) and $line_out[0] == '.') { + $line_out = '.' . $line_out; + } + $this->client_send($line_out . static::LE, 'DATA'); + } + } + + //Message data has been sent, complete the command + //Increase timelimit for end of DATA command + $savetimelimit = $this->Timelimit; + $this->Timelimit = $this->Timelimit * 2; + $result = $this->sendCommand('DATA END', '.', 250); + $this->recordLastTransactionID(); + //Restore timelimit + $this->Timelimit = $savetimelimit; + + return $result; + } + + /** + * Send an SMTP HELO or EHLO command. + * Used to identify the sending server to the receiving server. + * This makes sure that client and server are in a known state. + * Implements RFC 821: HELO + * and RFC 2821 EHLO. + * + * @param string $host The host name or IP to connect to + * + * @return bool + */ + public function hello($host = '') + { + //Try extended hello first (RFC 2821) + return $this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host); + } + + /** + * Send an SMTP HELO or EHLO command. + * Low-level implementation used by hello(). + * + * @param string $hello The HELO string + * @param string $host The hostname to say we are + * + * @return bool + * + * @see hello() + */ + protected function sendHello($hello, $host) + { + $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); + $this->helo_rply = $this->last_reply; + if ($noerror) { + $this->parseHelloFields($hello); + } else { + $this->server_caps = null; + } + + return $noerror; + } + + /** + * Parse a reply to HELO/EHLO command to discover server extensions. + * In case of HELO, the only parameter that can be discovered is a server name. + * + * @param string $type `HELO` or `EHLO` + */ + protected function parseHelloFields($type) + { + $this->server_caps = []; + $lines = explode("\n", $this->helo_rply); + + foreach ($lines as $n => $s) { + //First 4 chars contain response code followed by - or space + $s = trim(substr($s, 4)); + if (empty($s)) { + continue; + } + $fields = explode(' ', $s); + if (!empty($fields)) { + if (!$n) { + $name = $type; + $fields = $fields[0]; + } else { + $name = array_shift($fields); + switch ($name) { + case 'SIZE': + $fields = ($fields ? $fields[0] : 0); + break; + case 'AUTH': + if (!is_array($fields)) { + $fields = []; + } + break; + default: + $fields = true; + } + } + $this->server_caps[$name] = $fields; + } + } + } + + /** + * Send an SMTP MAIL command. + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more recipient + * commands may be called followed by a data command. + * Implements RFC 821: MAIL FROM: . + * + * @param string $from Source address of this message + * + * @return bool + */ + public function mail($from) + { + $useVerp = ($this->do_verp ? ' XVERP' : ''); + + return $this->sendCommand( + 'MAIL FROM', + 'MAIL FROM:<' . $from . '>' . $useVerp, + 250 + ); + } + + /** + * Send an SMTP QUIT command. + * Closes the socket if there is no error or the $close_on_error argument is true. + * Implements from RFC 821: QUIT . + * + * @param bool $close_on_error Should the connection close if an error occurs? + * + * @return bool + */ + public function quit($close_on_error = true) + { + $noerror = $this->sendCommand('QUIT', 'QUIT', 221); + $err = $this->error; //Save any error + if ($noerror or $close_on_error) { + $this->close(); + $this->error = $err; //Restore any error from the quit command + } + + return $noerror; + } + + /** + * Send an SMTP RCPT command. + * Sets the TO argument to $toaddr. + * Returns true if the recipient was accepted false if it was rejected. + * Implements from RFC 821: RCPT TO: . + * + * @param string $address The address the message is being sent to + * @param string $dsn Comma separated list of DSN notifications. NEVER, SUCCESS, FAILURE + * or DELAY. If you specify NEVER all other notifications are ignored. + * + * @return bool + */ + public function recipient($address, $dsn = '') + { + if (empty($dsn)) { + $rcpt = 'RCPT TO:<' . $address . '>'; + } else { + $dsn = strtoupper($dsn); + $notify = []; + + if (strpos($dsn, 'NEVER') !== false) { + $notify[] = 'NEVER'; + } else { + foreach (['SUCCESS', 'FAILURE', 'DELAY'] as $value) { + if (strpos($dsn, $value) !== false) { + $notify[] = $value; + } + } + } + + $rcpt = 'RCPT TO:<' . $address . '> NOTIFY=' . implode(',', $notify); + } + + return $this->sendCommand( + 'RCPT TO', + $rcpt, + [250, 251] + ); + } + + /** + * Send an SMTP RSET command. + * Abort any transaction that is currently in progress. + * Implements RFC 821: RSET . + * + * @return bool True on success + */ + public function reset() + { + return $this->sendCommand('RSET', 'RSET', 250); + } + + /** + * Send a command to an SMTP server and check its return code. + * + * @param string $command The command name - not sent to the server + * @param string $commandstring The actual command to send + * @param int|array $expect One or more expected integer success codes + * + * @return bool True on success + */ + protected function sendCommand($command, $commandstring, $expect) + { + if (!$this->connected()) { + $this->setError("Called $command without being connected"); + + return false; + } + //Reject line breaks in all commands + if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) { + $this->setError("Command '$command' contained line breaks"); + + return false; + } + $this->client_send($commandstring . static::LE, $command); + + $this->last_reply = $this->get_lines(); + // Fetch SMTP code and possible error code explanation + $matches = []; + if (preg_match('/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]{1,2}) )?/', $this->last_reply, $matches)) { + $code = $matches[1]; + $code_ex = (count($matches) > 2 ? $matches[2] : null); + // Cut off error code from each response line + $detail = preg_replace( + "/{$code}[ -]" . + ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . '/m', + '', + $this->last_reply + ); + } else { + // Fall back to simple parsing if regex fails + $code = substr($this->last_reply, 0, 3); + $code_ex = null; + $detail = substr($this->last_reply, 4); + } + + $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); + + if (!in_array($code, (array) $expect)) { + $this->setError( + "$command command failed", + $detail, + $code, + $code_ex + ); + $this->edebug( + 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply, + self::DEBUG_CLIENT + ); + + return false; + } + + $this->setError(''); + + return true; + } + + /** + * Send an SMTP SAML command. + * Starts a mail transaction from the email address specified in $from. + * Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more recipient + * commands may be called followed by a data command. This command + * will send the message to the users terminal if they are logged + * in and send them an email. + * Implements RFC 821: SAML FROM: . + * + * @param string $from The address the message is from + * + * @return bool + */ + public function sendAndMail($from) + { + return $this->sendCommand('SAML', "SAML FROM:$from", 250); + } + + /** + * Send an SMTP VRFY command. + * + * @param string $name The name to verify + * + * @return bool + */ + public function verify($name) + { + return $this->sendCommand('VRFY', "VRFY $name", [250, 251]); + } + + /** + * Send an SMTP NOOP command. + * Used to keep keep-alives alive, doesn't actually do anything. + * + * @return bool + */ + public function noop() + { + return $this->sendCommand('NOOP', 'NOOP', 250); + } + + /** + * Send an SMTP TURN command. + * This is an optional command for SMTP that this class does not support. + * This method is here to make the RFC821 Definition complete for this class + * and _may_ be implemented in future. + * Implements from RFC 821: TURN . + * + * @return bool + */ + public function turn() + { + $this->setError('The SMTP TURN command is not implemented'); + $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT); + + return false; + } + + /** + * Send raw data to the server. + * + * @param string $data The data to send + * @param string $command Optionally, the command this is part of, used only for controlling debug output + * + * @return int|bool The number of bytes sent to the server or false on error + */ + public function client_send($data, $command = '') + { + //If SMTP transcripts are left enabled, or debug output is posted online + //it can leak credentials, so hide credentials in all but lowest level + if (self::DEBUG_LOWLEVEL > $this->do_debug and + in_array($command, ['User & Password', 'Username', 'Password'], true)) { + $this->edebug('CLIENT -> SERVER:
+ * + * If the port component is not set or is the standard port for the current + * scheme, it SHOULD NOT be included. + * + * @see https://tools.ietf.org/html/rfc3986#section-3.2 + * @return string The URI authority, in "[user-info@]host[:port]" format. + */ + public function getAuthority(); + + /** + * Retrieve the user information component of the URI. + * + * If no user information is present, this method MUST return an empty + * string. + * + * If a user is present in the URI, this will return that value; + * additionally, if the password is also present, it will be appended to the + * user value, with a colon (":") separating the values. + * + * The trailing "@" character is not part of the user information and MUST + * NOT be added. + * + * @return string The URI user information, in "username[:password]" format. + */ + public function getUserInfo(); + + /** + * Retrieve the host component of the URI. + * + * If no host is present, this method MUST return an empty string. + * + * The value returned MUST be normalized to lowercase, per RFC 3986 + * Section 3.2.2. + * + * @see http://tools.ietf.org/html/rfc3986#section-3.2.2 + * @return string The URI host. + */ + public function getHost(); + + /** + * Retrieve the port component of the URI. + * + * If a port is present, and it is non-standard for the current scheme, + * this method MUST return it as an integer. If the port is the standard port + * used with the current scheme, this method SHOULD return null. + * + * If no port is present, and no scheme is present, this method MUST return + * a null value. + * + * If no port is present, but a scheme is present, this method MAY return + * the standard port for that scheme, but SHOULD return null. + * + * @return null|int The URI port. + */ + public function getPort(); + + /** + * Retrieve the path component of the URI. + * + * The path can either be empty or absolute (starting with a slash) or + * rootless (not starting with a slash). Implementations MUST support all + * three syntaxes. + * + * Normally, the empty path "" and absolute path "/" are considered equal as + * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically + * do this normalization because in contexts with a trimmed base path, e.g. + * the front controller, this difference becomes significant. It's the task + * of the user to handle both "" and "/". + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.3. + * + * As an example, if the value should include a slash ("/") not intended as + * delimiter between path segments, that value MUST be passed in encoded + * form (e.g., "%2F") to the instance. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.3 + * @return string The URI path. + */ + public function getPath(); + + /** + * Retrieve the query string of the URI. + * + * If no query string is present, this method MUST return an empty string. + * + * The leading "?" character is not part of the query and MUST NOT be + * added. + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.4. + * + * As an example, if a value in a key/value pair of the query string should + * include an ampersand ("&") not intended as a delimiter between values, + * that value MUST be passed in encoded form (e.g., "%26") to the instance. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.4 + * @return string The URI query string. + */ + public function getQuery(); + + /** + * Retrieve the fragment component of the URI. + * + * If no fragment is present, this method MUST return an empty string. + * + * The leading "#" character is not part of the fragment and MUST NOT be + * added. + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.5. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.5 + * @return string The URI fragment. + */ + public function getFragment(); + + /** + * Return an instance with the specified scheme. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified scheme. + * + * Implementations MUST support the schemes "http" and "https" case + * insensitively, and MAY accommodate other schemes if required. + * + * An empty scheme is equivalent to removing the scheme. + * + * @param string $scheme The scheme to use with the new instance. + * @return static A new instance with the specified scheme. + * @throws \InvalidArgumentException for invalid or unsupported schemes. + */ + public function withScheme($scheme); + + /** + * Return an instance with the specified user information. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified user information. + * + * Password is optional, but the user information MUST include the + * user; an empty string for the user is equivalent to removing user + * information. + * + * @param string $user The user name to use for authority. + * @param null|string $password The password associated with $user. + * @return static A new instance with the specified user information. + */ + public function withUserInfo($user, $password = null); + + /** + * Return an instance with the specified host. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified host. + * + * An empty host value is equivalent to removing the host. + * + * @param string $host The hostname to use with the new instance. + * @return static A new instance with the specified host. + * @throws \InvalidArgumentException for invalid hostnames. + */ + public function withHost($host); + + /** + * Return an instance with the specified port. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified port. + * + * Implementations MUST raise an exception for ports outside the + * established TCP and UDP port ranges. + * + * A null value provided for the port is equivalent to removing the port + * information. + * + * @param null|int $port The port to use with the new instance; a null value + * removes the port information. + * @return static A new instance with the specified port. + * @throws \InvalidArgumentException for invalid ports. + */ + public function withPort($port); + + /** + * Return an instance with the specified path. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified path. + * + * The path can either be empty or absolute (starting with a slash) or + * rootless (not starting with a slash). Implementations MUST support all + * three syntaxes. + * + * If the path is intended to be domain-relative rather than path relative then + * it must begin with a slash ("/"). Paths not starting with a slash ("/") + * are assumed to be relative to some base path known to the application or + * consumer. + * + * Users can provide both encoded and decoded path characters. + * Implementations ensure the correct encoding as outlined in getPath(). + * + * @param string $path The path to use with the new instance. + * @return static A new instance with the specified path. + * @throws \InvalidArgumentException for invalid paths. + */ + public function withPath($path); + + /** + * Return an instance with the specified query string. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified query string. + * + * Users can provide both encoded and decoded query characters. + * Implementations ensure the correct encoding as outlined in getQuery(). + * + * An empty query string value is equivalent to removing the query string. + * + * @param string $query The query string to use with the new instance. + * @return static A new instance with the specified query string. + * @throws \InvalidArgumentException for invalid query strings. + */ + public function withQuery($query); + + /** + * Return an instance with the specified URI fragment. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified URI fragment. + * + * Users can provide both encoded and decoded fragment characters. + * Implementations ensure the correct encoding as outlined in getFragment(). + * + * An empty fragment value is equivalent to removing the fragment. + * + * @param string $fragment The fragment to use with the new instance. + * @return static A new instance with the specified fragment. + */ + public function withFragment($fragment); + + /** + * Return the string representation as a URI reference. + * + * Depending on which components of the URI are present, the resulting + * string is either a full URI or relative reference according to RFC 3986, + * Section 4.1. The method concatenates the various components of the URI, + * using the appropriate delimiters: + * + * - If a scheme is present, it MUST be suffixed by ":". + * - If an authority is present, it MUST be prefixed by "//". + * - The path can be concatenated without delimiters. But there are two + * cases where the path has to be adjusted to make the URI reference + * valid as PHP does not allow to throw an exception in __toString(): + * - If the path is rootless and an authority is present, the path MUST + * be prefixed by "/". + * - If the path is starting with more than one "/" and no authority is + * present, the starting slashes MUST be reduced to one. + * - If a query is present, it MUST be prefixed by "?". + * - If a fragment is present, it MUST be prefixed by "#". + * + * @see http://tools.ietf.org/html/rfc3986#section-4.1 + * @return string + */ + public function __toString(); +} diff --git a/vendor/robrichards/xmlseclibs/CHANGELOG.txt b/vendor/robrichards/xmlseclibs/CHANGELOG.txt new file mode 100644 index 0000000000..faaf61cf42 --- /dev/null +++ b/vendor/robrichards/xmlseclibs/CHANGELOG.txt @@ -0,0 +1,207 @@ +xmlseclibs.php +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +06, Nov 2019, 3.0.4 +Security Improvements: +- Insure only a single SignedInfo element exists within a signature during + verification. Refs CVE-2019-3465. +Bug Fixes: +- Fix variable casing. + +15, Nov 2018, 3.0.3 +Bug Fixes: +- Fix casing of class name. (Willem Stuursma-Ruwen) +- Fix Xpath casing. (Tim van Dijen) + +Improvements: +- Make PCRE2 compliant. (Stefan Winter) +- Add PHP 7.3 support. (Stefan Winter) + +27, Sep 2018, 3.0.2 +Security Improvements: +- OpenSSL is now a requirement rather than suggestion. (Slaven Bacelic) +- Filter input to avoid XPath injection. (Jaime Pérez) + +Bug Fixes: +- Fix missing parentheses (Tim van Dijen) + +Improvements: +- Use strict comparison operator to compare digest values. (Jaime Pérez) +- Remove call to file_get_contents that doesn't even work. (Jaime Pérez) +- Document potentially dangerous return value behaviour. (Thijs Kinkhorst) + +31, Aug 2017, 3.0.1 +Bug Fixes: +- Fixed missing () in function call. (Dennis Væversted) + +Improvements: +- Add OneLogin to supported software. +- Add .gitattributes to remove unneeded files. (Filippo Tessarotto) +- Fix bug in example code. (Dan Church) +- Travis: add PHP 7.1, move hhvm to allowed failures. (Thijs Kinkhorst) +- Drop failing extract-win-cert test (Thijs Kinkhorst). (Thijs Kinkhorst) +- Add comments to warn about return values of verify(). (Thijs Kinkhorst) +- Fix tests to properly check return code of verify(). (Thijs Kinkhorst) +- Restore support for PHP >= 5.4. (Jaime Pérez) + +25, May 2017, 3.0.0 +Improvements: +- Remove use of mcrypt (skymeyer) + +08, Sep 2016, 2.0.1 +Bug Fixes: +- Strip whitespace characters when parsing X509Certificate. fixes #84 + (klemen.bratec) +- Certificate 'subject' values can be arrays. fixes #80 (Andreas Stangl) +- HHVM signing node with ID attribute w/out namespace regenerates ID value. + fixes #88 (Milos Tomic) + +Improvements: +- Fix typos and add some PHPDoc Blocks. (gfaust-qb) +- Update lightSAML link. (Milos Tomic) +- Update copyright dates. + +31, Jul 2015, 2.0.0 +Features: +- Namespace support. Classes now in the RobRichards\XMLSecLibs\ namespace. + +Improvements: +- Dropped support for PHP 5.2 + +31, Jul 2015, 1.4.1 +Bug Fixes: +- Allow for large digest values that may have line breaks. fixes #62 + +Features: +- Support for locating specific signature when multiple exist in + document. (griga3k) + +Improvements: +- Add optional argument to XMLSecurityDSig to define the prefix to be used, + also allowing for null to use no prefix, for the dsig namespace. fixes #13 +- Code cleanup +- Depreciated XMLSecurityDSig::generate_GUID for XMLSecurityDSig::generateGUID + +23, Jun 2015, 1.4.0 +Features: +- Support for PSR-0 standard. +- Support for X509SubjectName. (Milos Tomic) +- Add HMAC-SHA1 support. + +Improvements: +- Add how to install to README. (Bernardo Vieira da Silva) +- Code cleanup. (Jaime Pérez) +- Normalilze tests. (Hidde Wieringa) +- Add basic usage to README. (Hidde Wieringa) + +21, May 2015, 1.3.2 +Bug Fixes: +- Fix Undefined variable notice. (dpieper85) +- Fix typo when setting MimeType attribute. (Eugene OZ) +- Fix validateReference() with enveloping signatures + +Features: +- canonicalizeData performance optimization. (Jaime Pérez) +- Add composer support (Maks3w) + +19, Jun 2013, 1.3.1 +Features: +- return encrypted node from XMLSecEnc::encryptNode() when replace is set to + false. (Olav) +- Add support for RSA SHA384 and RSA_SHA512 and SHA384 digest. (Jaime PŽrez) +- Add options parameter to the add cert methods. +- Add optional issuerSerial creation with cert + +Bug Fixes: +- Fix persisted Id when namespaced. (Koen Thomeer) + +Improvements: +- Add LICENSE file +- Convert CHANGELOG.txt to UTF-8 + +26, Sep 2011, 1.3.0 +Features: +- Add param to append sig to node when signing. Fixes a problem when using + inclusive canonicalization to append a signature within a namespaced subtree. + ex. $objDSig->sign($objKey, $appendToNode); +- Add ability to encrypt by reference +- Add support for refences within an encrypted key +- Add thumbprint generation capability (XMLSecurityKey->getX509Thumbprint() and + XMLSecurityKey::getRawThumbprint($cert)) +- Return signature element node from XMLSecurityDSig::insertSignature() and + XMLSecurityDSig::appendSignature() methods +- Support for with simple URI Id reference. +- Add XMLSecurityKey::getSymmetricKeySize() method (Olav) +- Add XMLSecEnc::getCipherValue() method (Olav) +- Improve XMLSecurityKey:generateSessionKey() logic (Olav) + +Bug Fixes: +- Change split() to explode() as split is now depreciated +- ds:References using empty or simple URI Id reference should never include + comments in canonicalized data. +- Make sure that the elements in EncryptedData are emitted in the correct + sequence. + +11 Jan 2010, 1.2.2 +Features: +- Add support XPath support when creating signature. Provides support for + working with EBXML documents. +- Add reference option to force creation of URI attribute. For use + when adding a DOM Document where by default no URI attribute is added. +- Add support for RSA-SHA256 + +Bug Fixes: +- fix bug #5: createDOMDocumentFragment() in decryptNode when data is node + content (patch by Francois Wang) + + +08 Jul 2008, 1.2.1 +Features: +- Attempt to use mhash when hash extension is not present. (Alfredo Cubitos). +- Add fallback to built-in sha1 if both hash and mhash are not available and + throw error for other for other missing hashes. (patch by Olav Morken). +- Add getX509Certificate method to retrieve the x509 cert used for Key. + (patch by Olav Morken). +- Add getValidatedNodes method to retrieve the elements signed by the + signature. (patch by Olav Morken). +- Add insertSignature method for precision signature insertion. Merge + functionality from appendSignature in the process. (Olav Morken, Rob). +- Finally add some tests + +Bug Fixes: +- Fix canonicalization for Document node when using PHP < 5.2. +- Add padding for RSA_SHA1. (patch by Olav Morken). + + +27 Nov 2007, 1.2.0 +Features: +- New addReference/List option (overwrite). Boolean flag indicating if URI + value should be overwritten if already existing within document. + Default is TRUE to maintain BC. + +18 Nov 2007, 1.1.2 +Bug Fixes: +- Remove closing PHP tag to fix extra whitespace characters from being output + +11 Nov 2007, 1.1.1 +Features: +- Add getRefNodeID() and getRefIDs() methods missed in previous release. + Provide functionality to find URIs of existing reference nodes. + Required by simpleSAMLphp project + +Bug Fixes: +- Remove erroneous whitespace causing issues under certain circumastances. + +18 Oct 2007, 1.1.0 +Features: +- Enable creation of enveloping signature. This allows the creation of + managed information cards. +- Add addObject method for enveloping signatures. +- Add staticGet509XCerts method. Chained certificates within a PEM file can + now be added within the X509Data node. +- Add xpath support within transformations +- Add InclusiveNamespaces prefix list support within exclusive transformations. + +Bug Fixes: +- Initialize random number generator for mcrypt_create_iv. (Joan Cornadó). +- Fix an interoperability issue with .NET when encrypting data in CBC mode. + (Joan Cornadó). diff --git a/vendor/robrichards/xmlseclibs/LICENSE b/vendor/robrichards/xmlseclibs/LICENSE new file mode 100644 index 0000000000..4fe5e5ffbd --- /dev/null +++ b/vendor/robrichards/xmlseclibs/LICENSE @@ -0,0 +1,31 @@ +Copyright (c) 2007-2019, Robert Richards . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Robert Richards nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/robrichards/xmlseclibs/README.md b/vendor/robrichards/xmlseclibs/README.md new file mode 100644 index 0000000000..1a83735ba5 --- /dev/null +++ b/vendor/robrichards/xmlseclibs/README.md @@ -0,0 +1,84 @@ +#xmlseclibs + +xmlseclibs is a library written in PHP for working with XML Encryption and Signatures. + +The author of xmlseclibs is Rob Richards. + +# Branches +Master is currently the only actively maintained branch. +* master: Removes mcrypt usage requiring 5.4+ (5.6.24+ recommended for security reasons) +* 2.0: Contains namespace support requiring 5.3+ +* 1.4: Contains auto-loader support while also maintaining backwards compatiblity with the older 1.3 version using the xmlseclibs.php file. Supports PHP 5.2+ + +# Requirements + +xmlseclibs requires PHP version 5.4 or greater. **5.6.24+ recommended for security reasons** + + +## How to Install + +Install with [`composer.phar`](http://getcomposer.org). + +```sh +php composer.phar require "robrichards/xmlseclibs" +``` + + +## Use cases + +xmlseclibs is being used in many different software. + +* [SimpleSAMLPHP](https://github.com/simplesamlphp/simplesamlphp) +* [LightSAML](https://github.com/lightsaml/lightsaml) +* [OneLogin](https://github.com/onelogin/php-saml) + +## Basic usage + +The example below shows basic usage of xmlseclibs, with a SHA-256 signature. + +```php +use RobRichards\XMLSecLibs\XMLSecurityDSig; +use RobRichards\XMLSecLibs\XMLSecurityKey; + +// Load the XML to be signed +$doc = new DOMDocument(); +$doc->load('./path/to/file/tobesigned.xml'); + +// Create a new Security object +$objDSig = new XMLSecurityDSig(); +// Use the c14n exclusive canonicalization +$objDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N); +// Sign using SHA-256 +$objDSig->addReference( + $doc, + XMLSecurityDSig::SHA256, + array('http://www.w3.org/2000/09/xmldsig#enveloped-signature') +); + +// Create a new (private) Security key +$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, array('type'=>'private')); +/* +If key has a passphrase, set it using +$objKey->passphrase = ''; +*/ +// Load the private key +$objKey->loadKey('./path/to/privatekey.pem', TRUE); + +// Sign the XML file +$objDSig->sign($objKey); + +// Add the associated public key to the signature +$objDSig->add509Cert(file_get_contents('./path/to/file/mycert.pem')); + +// Append the signature to the XML +$objDSig->appendSignature($doc->documentElement); +// Save the signed XML +$doc->save('./path/to/signed.xml'); +``` + +## How to Contribute + +* [Open Issues](https://github.com/robrichards/xmlseclibs/issues) +* [Open Pull Requests](https://github.com/robrichards/xmlseclibs/pulls) + +Mailing List: https://groups.google.com/forum/#!forum/xmlseclibs diff --git a/vendor/robrichards/xmlseclibs/composer.json b/vendor/robrichards/xmlseclibs/composer.json new file mode 100644 index 0000000000..22ce7a3e1f --- /dev/null +++ b/vendor/robrichards/xmlseclibs/composer.json @@ -0,0 +1,21 @@ +{ + "name": "robrichards/xmlseclibs", + "description": "A PHP library for XML Security", + "license": "BSD-3-Clause", + "keywords": [ + "xml", + "xmldsig", + "signature", + "security" + ], + "homepage": "https://github.com/robrichards/xmlseclibs", + "autoload": { + "psr-4": { + "RobRichards\\XMLSecLibs\\": "src" + } + }, + "require": { + "php": ">= 5.4", + "ext-openssl": "*" + } +} diff --git a/vendor/robrichards/xmlseclibs/src/Utils/XPath.php b/vendor/robrichards/xmlseclibs/src/Utils/XPath.php new file mode 100644 index 0000000000..8cdc48e157 --- /dev/null +++ b/vendor/robrichards/xmlseclibs/src/Utils/XPath.php @@ -0,0 +1,44 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Robert Richards nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @author Robert Richards + * @copyright 2007-2019 Robert Richards + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + */ + +class XMLSecEnc +{ + const template = " + + + +"; + + const Element = 'http://www.w3.org/2001/04/xmlenc#Element'; + const Content = 'http://www.w3.org/2001/04/xmlenc#Content'; + const URI = 3; + const XMLENCNS = 'http://www.w3.org/2001/04/xmlenc#'; + + /** @var null|DOMDocument */ + private $encdoc = null; + + /** @var null|DOMNode */ + private $rawNode = null; + + /** @var null|string */ + public $type = null; + + /** @var null|DOMElement */ + public $encKey = null; + + /** @var array */ + private $references = array(); + + public function __construct() + { + $this->_resetTemplate(); + } + + private function _resetTemplate() + { + $this->encdoc = new DOMDocument(); + $this->encdoc->loadXML(self::template); + } + + /** + * @param string $name + * @param DOMNode $node + * @param string $type + * @throws Exception + */ + public function addReference($name, $node, $type) + { + if (! $node instanceOf DOMNode) { + throw new Exception('$node is not of type DOMNode'); + } + $curencdoc = $this->encdoc; + $this->_resetTemplate(); + $encdoc = $this->encdoc; + $this->encdoc = $curencdoc; + $refuri = XMLSecurityDSig::generateGUID(); + $element = $encdoc->documentElement; + $element->setAttribute("Id", $refuri); + $this->references[$name] = array("node" => $node, "type" => $type, "encnode" => $encdoc, "refuri" => $refuri); + } + + /** + * @param DOMNode $node + */ + public function setNode($node) + { + $this->rawNode = $node; + } + + /** + * Encrypt the selected node with the given key. + * + * @param XMLSecurityKey $objKey The encryption key and algorithm. + * @param bool $replace Whether the encrypted node should be replaced in the original tree. Default is true. + * @throws Exception + * + * @return DOMElement The -element. + */ + public function encryptNode($objKey, $replace = true) + { + $data = ''; + if (empty($this->rawNode)) { + throw new Exception('Node to encrypt has not been set'); + } + if (! $objKey instanceof XMLSecurityKey) { + throw new Exception('Invalid Key'); + } + $doc = $this->rawNode->ownerDocument; + $xPath = new DOMXPath($this->encdoc); + $objList = $xPath->query('/xenc:EncryptedData/xenc:CipherData/xenc:CipherValue'); + $cipherValue = $objList->item(0); + if ($cipherValue == null) { + throw new Exception('Error locating CipherValue element within template'); + } + switch ($this->type) { + case (self::Element): + $data = $doc->saveXML($this->rawNode); + $this->encdoc->documentElement->setAttribute('Type', self::Element); + break; + case (self::Content): + $children = $this->rawNode->childNodes; + foreach ($children AS $child) { + $data .= $doc->saveXML($child); + } + $this->encdoc->documentElement->setAttribute('Type', self::Content); + break; + default: + throw new Exception('Type is currently not supported'); + } + + $encMethod = $this->encdoc->documentElement->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:EncryptionMethod')); + $encMethod->setAttribute('Algorithm', $objKey->getAlgorithm()); + $cipherValue->parentNode->parentNode->insertBefore($encMethod, $cipherValue->parentNode->parentNode->firstChild); + + $strEncrypt = base64_encode($objKey->encryptData($data)); + $value = $this->encdoc->createTextNode($strEncrypt); + $cipherValue->appendChild($value); + + if ($replace) { + switch ($this->type) { + case (self::Element): + if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) { + return $this->encdoc; + } + $importEnc = $this->rawNode->ownerDocument->importNode($this->encdoc->documentElement, true); + $this->rawNode->parentNode->replaceChild($importEnc, $this->rawNode); + return $importEnc; + case (self::Content): + $importEnc = $this->rawNode->ownerDocument->importNode($this->encdoc->documentElement, true); + while ($this->rawNode->firstChild) { + $this->rawNode->removeChild($this->rawNode->firstChild); + } + $this->rawNode->appendChild($importEnc); + return $importEnc; + } + } else { + return $this->encdoc->documentElement; + } + } + + /** + * @param XMLSecurityKey $objKey + * @throws Exception + */ + public function encryptReferences($objKey) + { + $curRawNode = $this->rawNode; + $curType = $this->type; + foreach ($this->references AS $name => $reference) { + $this->encdoc = $reference["encnode"]; + $this->rawNode = $reference["node"]; + $this->type = $reference["type"]; + try { + $encNode = $this->encryptNode($objKey); + $this->references[$name]["encnode"] = $encNode; + } catch (Exception $e) { + $this->rawNode = $curRawNode; + $this->type = $curType; + throw $e; + } + } + $this->rawNode = $curRawNode; + $this->type = $curType; + } + + /** + * Retrieve the CipherValue text from this encrypted node. + * + * @throws Exception + * @return string|null The Ciphervalue text, or null if no CipherValue is found. + */ + public function getCipherValue() + { + if (empty($this->rawNode)) { + throw new Exception('Node to decrypt has not been set'); + } + + $doc = $this->rawNode->ownerDocument; + $xPath = new DOMXPath($doc); + $xPath->registerNamespace('xmlencr', self::XMLENCNS); + /* Only handles embedded content right now and not a reference */ + $query = "./xmlencr:CipherData/xmlencr:CipherValue"; + $nodeset = $xPath->query($query, $this->rawNode); + $node = $nodeset->item(0); + + if (!$node) { + return null; + } + + return base64_decode($node->nodeValue); + } + + /** + * Decrypt this encrypted node. + * + * The behaviour of this function depends on the value of $replace. + * If $replace is false, we will return the decrypted data as a string. + * If $replace is true, we will insert the decrypted element(s) into the + * document, and return the decrypted element(s). + * + * @param XMLSecurityKey $objKey The decryption key that should be used when decrypting the node. + * @param boolean $replace Whether we should replace the encrypted node in the XML document with the decrypted data. The default is true. + * + * @return string|DOMElement The decrypted data. + */ + public function decryptNode($objKey, $replace=true) + { + if (! $objKey instanceof XMLSecurityKey) { + throw new Exception('Invalid Key'); + } + + $encryptedData = $this->getCipherValue(); + if ($encryptedData) { + $decrypted = $objKey->decryptData($encryptedData); + if ($replace) { + switch ($this->type) { + case (self::Element): + $newdoc = new DOMDocument(); + $newdoc->loadXML($decrypted); + if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) { + return $newdoc; + } + $importEnc = $this->rawNode->ownerDocument->importNode($newdoc->documentElement, true); + $this->rawNode->parentNode->replaceChild($importEnc, $this->rawNode); + return $importEnc; + case (self::Content): + if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) { + $doc = $this->rawNode; + } else { + $doc = $this->rawNode->ownerDocument; + } + $newFrag = $doc->createDocumentFragment(); + $newFrag->appendXML($decrypted); + $parent = $this->rawNode->parentNode; + $parent->replaceChild($newFrag, $this->rawNode); + return $parent; + default: + return $decrypted; + } + } else { + return $decrypted; + } + } else { + throw new Exception("Cannot locate encrypted data"); + } + } + + /** + * Encrypt the XMLSecurityKey + * + * @param XMLSecurityKey $srcKey + * @param XMLSecurityKey $rawKey + * @param bool $append + * @throws Exception + */ + public function encryptKey($srcKey, $rawKey, $append=true) + { + if ((! $srcKey instanceof XMLSecurityKey) || (! $rawKey instanceof XMLSecurityKey)) { + throw new Exception('Invalid Key'); + } + $strEncKey = base64_encode($srcKey->encryptData($rawKey->key)); + $root = $this->encdoc->documentElement; + $encKey = $this->encdoc->createElementNS(self::XMLENCNS, 'xenc:EncryptedKey'); + if ($append) { + $keyInfo = $root->insertBefore($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo'), $root->firstChild); + $keyInfo->appendChild($encKey); + } else { + $this->encKey = $encKey; + } + $encMethod = $encKey->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:EncryptionMethod')); + $encMethod->setAttribute('Algorithm', $srcKey->getAlgorith()); + if (! empty($srcKey->name)) { + $keyInfo = $encKey->appendChild($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo')); + $keyInfo->appendChild($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyName', $srcKey->name)); + } + $cipherData = $encKey->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:CipherData')); + $cipherData->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:CipherValue', $strEncKey)); + if (is_array($this->references) && count($this->references) > 0) { + $refList = $encKey->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:ReferenceList')); + foreach ($this->references AS $name => $reference) { + $refuri = $reference["refuri"]; + $dataRef = $refList->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:DataReference')); + $dataRef->setAttribute("URI", '#' . $refuri); + } + } + return; + } + + /** + * @param XMLSecurityKey $encKey + * @return DOMElement|string + * @throws Exception + */ + public function decryptKey($encKey) + { + if (! $encKey->isEncrypted) { + throw new Exception("Key is not Encrypted"); + } + if (empty($encKey->key)) { + throw new Exception("Key is missing data to perform the decryption"); + } + return $this->decryptNode($encKey, false); + } + + /** + * @param DOMDocument $element + * @return DOMNode|null + */ + public function locateEncryptedData($element) + { + if ($element instanceof DOMDocument) { + $doc = $element; + } else { + $doc = $element->ownerDocument; + } + if ($doc) { + $xpath = new DOMXPath($doc); + $query = "//*[local-name()='EncryptedData' and namespace-uri()='".self::XMLENCNS."']"; + $nodeset = $xpath->query($query); + return $nodeset->item(0); + } + return null; + } + + /** + * Returns the key from the DOM + * @param null|DOMNode $node + * @return null|XMLSecurityKey + */ + public function locateKey($node=null) + { + if (empty($node)) { + $node = $this->rawNode; + } + if (! $node instanceof DOMNode) { + return null; + } + if ($doc = $node->ownerDocument) { + $xpath = new DOMXPath($doc); + $xpath->registerNamespace('xmlsecenc', self::XMLENCNS); + $query = ".//xmlsecenc:EncryptionMethod"; + $nodeset = $xpath->query($query, $node); + if ($encmeth = $nodeset->item(0)) { + $attrAlgorithm = $encmeth->getAttribute("Algorithm"); + try { + $objKey = new XMLSecurityKey($attrAlgorithm, array('type' => 'private')); + } catch (Exception $e) { + return null; + } + return $objKey; + } + } + return null; + } + + /** + * @param null|XMLSecurityKey $objBaseKey + * @param null|DOMNode $node + * @return null|XMLSecurityKey + * @throws Exception + */ + public static function staticLocateKeyInfo($objBaseKey=null, $node=null) + { + if (empty($node) || (! $node instanceof DOMNode)) { + return null; + } + $doc = $node->ownerDocument; + if (!$doc) { + return null; + } + + $xpath = new DOMXPath($doc); + $xpath->registerNamespace('xmlsecenc', self::XMLENCNS); + $xpath->registerNamespace('xmlsecdsig', XMLSecurityDSig::XMLDSIGNS); + $query = "./xmlsecdsig:KeyInfo"; + $nodeset = $xpath->query($query, $node); + $encmeth = $nodeset->item(0); + if (!$encmeth) { + /* No KeyInfo in EncryptedData / EncryptedKey. */ + return $objBaseKey; + } + + foreach ($encmeth->childNodes AS $child) { + switch ($child->localName) { + case 'KeyName': + if (! empty($objBaseKey)) { + $objBaseKey->name = $child->nodeValue; + } + break; + case 'KeyValue': + foreach ($child->childNodes AS $keyval) { + switch ($keyval->localName) { + case 'DSAKeyValue': + throw new Exception("DSAKeyValue currently not supported"); + case 'RSAKeyValue': + $modulus = null; + $exponent = null; + if ($modulusNode = $keyval->getElementsByTagName('Modulus')->item(0)) { + $modulus = base64_decode($modulusNode->nodeValue); + } + if ($exponentNode = $keyval->getElementsByTagName('Exponent')->item(0)) { + $exponent = base64_decode($exponentNode->nodeValue); + } + if (empty($modulus) || empty($exponent)) { + throw new Exception("Missing Modulus or Exponent"); + } + $publicKey = XMLSecurityKey::convertRSA($modulus, $exponent); + $objBaseKey->loadKey($publicKey); + break; + } + } + break; + case 'RetrievalMethod': + $type = $child->getAttribute('Type'); + if ($type !== 'http://www.w3.org/2001/04/xmlenc#EncryptedKey') { + /* Unsupported key type. */ + break; + } + $uri = $child->getAttribute('URI'); + if ($uri[0] !== '#') { + /* URI not a reference - unsupported. */ + break; + } + $id = substr($uri, 1); + + $query = '//xmlsecenc:EncryptedKey[@Id="'.XPath::filterAttrValue($id, XPath::DOUBLE_QUOTE).'"]'; + $keyElement = $xpath->query($query)->item(0); + if (!$keyElement) { + throw new Exception("Unable to locate EncryptedKey with @Id='$id'."); + } + + return XMLSecurityKey::fromEncryptedKeyElement($keyElement); + case 'EncryptedKey': + return XMLSecurityKey::fromEncryptedKeyElement($child); + case 'X509Data': + if ($x509certNodes = $child->getElementsByTagName('X509Certificate')) { + if ($x509certNodes->length > 0) { + $x509cert = $x509certNodes->item(0)->textContent; + $x509cert = str_replace(array("\r", "\n", " "), "", $x509cert); + $x509cert = "-----BEGIN CERTIFICATE-----\n".chunk_split($x509cert, 64, "\n")."-----END CERTIFICATE-----\n"; + $objBaseKey->loadKey($x509cert, false, true); + } + } + break; + } + } + return $objBaseKey; + } + + /** + * @param null|XMLSecurityKey $objBaseKey + * @param null|DOMNode $node + * @return null|XMLSecurityKey + */ + public function locateKeyInfo($objBaseKey=null, $node=null) + { + if (empty($node)) { + $node = $this->rawNode; + } + return self::staticLocateKeyInfo($objBaseKey, $node); + } +} diff --git a/vendor/robrichards/xmlseclibs/src/XMLSecurityDSig.php b/vendor/robrichards/xmlseclibs/src/XMLSecurityDSig.php new file mode 100644 index 0000000000..c9063d0f59 --- /dev/null +++ b/vendor/robrichards/xmlseclibs/src/XMLSecurityDSig.php @@ -0,0 +1,1150 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Robert Richards nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @author Robert Richards + * @copyright 2007-2019 Robert Richards + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + */ + +class XMLSecurityDSig +{ + const XMLDSIGNS = 'http://www.w3.org/2000/09/xmldsig#'; + const SHA1 = 'http://www.w3.org/2000/09/xmldsig#sha1'; + const SHA256 = 'http://www.w3.org/2001/04/xmlenc#sha256'; + const SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#sha384'; + const SHA512 = 'http://www.w3.org/2001/04/xmlenc#sha512'; + const RIPEMD160 = 'http://www.w3.org/2001/04/xmlenc#ripemd160'; + + const C14N = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'; + const C14N_COMMENTS = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments'; + const EXC_C14N = 'http://www.w3.org/2001/10/xml-exc-c14n#'; + const EXC_C14N_COMMENTS = 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments'; + + const template = ' + + + +'; + + const BASE_TEMPLATE = ' + + + +'; + + /** @var DOMElement|null */ + public $sigNode = null; + + /** @var array */ + public $idKeys = array(); + + /** @var array */ + public $idNS = array(); + + /** @var string|null */ + private $signedInfo = null; + + /** @var DomXPath|null */ + private $xPathCtx = null; + + /** @var string|null */ + private $canonicalMethod = null; + + /** @var string */ + private $prefix = ''; + + /** @var string */ + private $searchpfx = 'secdsig'; + + /** + * This variable contains an associative array of validated nodes. + * @var array|null + */ + private $validatedNodes = null; + + /** + * @param string $prefix + */ + public function __construct($prefix='ds') + { + $template = self::BASE_TEMPLATE; + if (! empty($prefix)) { + $this->prefix = $prefix.':'; + $search = array("ownerDocument; + } + if ($doc) { + $xpath = new DOMXPath($doc); + $xpath->registerNamespace('secdsig', self::XMLDSIGNS); + $query = ".//secdsig:Signature"; + $nodeset = $xpath->query($query, $objDoc); + $this->sigNode = $nodeset->item($pos); + $query = "./secdsig:SignedInfo"; + $nodeset = $xpath->query($query, $this->sigNode); + if ($nodeset->length > 1) { + throw new Exception("Invalid structure - Too many SignedInfo elements found"); + } + return $this->sigNode; + } + return null; + } + + /** + * @param string $name + * @param null|string $value + * @return DOMElement + */ + public function createNewSignNode($name, $value=null) + { + $doc = $this->sigNode->ownerDocument; + if (! is_null($value)) { + $node = $doc->createElementNS(self::XMLDSIGNS, $this->prefix.$name, $value); + } else { + $node = $doc->createElementNS(self::XMLDSIGNS, $this->prefix.$name); + } + return $node; + } + + /** + * @param string $method + * @throws Exception + */ + public function setCanonicalMethod($method) + { + switch ($method) { + case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315': + case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments': + case 'http://www.w3.org/2001/10/xml-exc-c14n#': + case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments': + $this->canonicalMethod = $method; + break; + default: + throw new Exception('Invalid Canonical Method'); + } + if ($xpath = $this->getXPathObj()) { + $query = './'.$this->searchpfx.':SignedInfo'; + $nodeset = $xpath->query($query, $this->sigNode); + if ($sinfo = $nodeset->item(0)) { + $query = './'.$this->searchpfx.'CanonicalizationMethod'; + $nodeset = $xpath->query($query, $sinfo); + if (! ($canonNode = $nodeset->item(0))) { + $canonNode = $this->createNewSignNode('CanonicalizationMethod'); + $sinfo->insertBefore($canonNode, $sinfo->firstChild); + } + $canonNode->setAttribute('Algorithm', $this->canonicalMethod); + } + } + } + + /** + * @param DOMNode $node + * @param string $canonicalmethod + * @param null|array $arXPath + * @param null|array $prefixList + * @return string + */ + private function canonicalizeData($node, $canonicalmethod, $arXPath=null, $prefixList=null) + { + $exclusive = false; + $withComments = false; + switch ($canonicalmethod) { + case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315': + $exclusive = false; + $withComments = false; + break; + case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments': + $withComments = true; + break; + case 'http://www.w3.org/2001/10/xml-exc-c14n#': + $exclusive = true; + break; + case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments': + $exclusive = true; + $withComments = true; + break; + } + + if (is_null($arXPath) && ($node instanceof DOMNode) && ($node->ownerDocument !== null) && $node->isSameNode($node->ownerDocument->documentElement)) { + /* Check for any PI or comments as they would have been excluded */ + $element = $node; + while ($refnode = $element->previousSibling) { + if ($refnode->nodeType == XML_PI_NODE || (($refnode->nodeType == XML_COMMENT_NODE) && $withComments)) { + break; + } + $element = $refnode; + } + if ($refnode == null) { + $node = $node->ownerDocument; + } + } + + return $node->C14N($exclusive, $withComments, $arXPath, $prefixList); + } + + /** + * @return null|string + */ + public function canonicalizeSignedInfo() + { + + $doc = $this->sigNode->ownerDocument; + $canonicalmethod = null; + if ($doc) { + $xpath = $this->getXPathObj(); + $query = "./secdsig:SignedInfo"; + $nodeset = $xpath->query($query, $this->sigNode); + if ($nodeset->length > 1) { + throw new Exception("Invalid structure - Too many SignedInfo elements found"); + } + if ($signInfoNode = $nodeset->item(0)) { + $query = "./secdsig:CanonicalizationMethod"; + $nodeset = $xpath->query($query, $signInfoNode); + if ($canonNode = $nodeset->item(0)) { + $canonicalmethod = $canonNode->getAttribute('Algorithm'); + } + $this->signedInfo = $this->canonicalizeData($signInfoNode, $canonicalmethod); + return $this->signedInfo; + } + } + return null; + } + + /** + * @param string $digestAlgorithm + * @param string $data + * @param bool $encode + * @return string + * @throws Exception + */ + public function calculateDigest($digestAlgorithm, $data, $encode = true) + { + switch ($digestAlgorithm) { + case self::SHA1: + $alg = 'sha1'; + break; + case self::SHA256: + $alg = 'sha256'; + break; + case self::SHA384: + $alg = 'sha384'; + break; + case self::SHA512: + $alg = 'sha512'; + break; + case self::RIPEMD160: + $alg = 'ripemd160'; + break; + default: + throw new Exception("Cannot validate digest: Unsupported Algorithm <$digestAlgorithm>"); + } + + $digest = hash($alg, $data, true); + if ($encode) { + $digest = base64_encode($digest); + } + return $digest; + + } + + /** + * @param $refNode + * @param string $data + * @return bool + */ + public function validateDigest($refNode, $data) + { + $xpath = new DOMXPath($refNode->ownerDocument); + $xpath->registerNamespace('secdsig', self::XMLDSIGNS); + $query = 'string(./secdsig:DigestMethod/@Algorithm)'; + $digestAlgorithm = $xpath->evaluate($query, $refNode); + $digValue = $this->calculateDigest($digestAlgorithm, $data, false); + $query = 'string(./secdsig:DigestValue)'; + $digestValue = $xpath->evaluate($query, $refNode); + return ($digValue === base64_decode($digestValue)); + } + + /** + * @param $refNode + * @param DOMNode $objData + * @param bool $includeCommentNodes + * @return string + */ + public function processTransforms($refNode, $objData, $includeCommentNodes = true) + { + $data = $objData; + $xpath = new DOMXPath($refNode->ownerDocument); + $xpath->registerNamespace('secdsig', self::XMLDSIGNS); + $query = './secdsig:Transforms/secdsig:Transform'; + $nodelist = $xpath->query($query, $refNode); + $canonicalMethod = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'; + $arXPath = null; + $prefixList = null; + foreach ($nodelist AS $transform) { + $algorithm = $transform->getAttribute("Algorithm"); + switch ($algorithm) { + case 'http://www.w3.org/2001/10/xml-exc-c14n#': + case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments': + + if (!$includeCommentNodes) { + /* We remove comment nodes by forcing it to use a canonicalization + * without comments. + */ + $canonicalMethod = 'http://www.w3.org/2001/10/xml-exc-c14n#'; + } else { + $canonicalMethod = $algorithm; + } + + $node = $transform->firstChild; + while ($node) { + if ($node->localName == 'InclusiveNamespaces') { + if ($pfx = $node->getAttribute('PrefixList')) { + $arpfx = array(); + $pfxlist = explode(" ", $pfx); + foreach ($pfxlist AS $pfx) { + $val = trim($pfx); + if (! empty($val)) { + $arpfx[] = $val; + } + } + if (count($arpfx) > 0) { + $prefixList = $arpfx; + } + } + break; + } + $node = $node->nextSibling; + } + break; + case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315': + case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments': + if (!$includeCommentNodes) { + /* We remove comment nodes by forcing it to use a canonicalization + * without comments. + */ + $canonicalMethod = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'; + } else { + $canonicalMethod = $algorithm; + } + + break; + case 'http://www.w3.org/TR/1999/REC-xpath-19991116': + $node = $transform->firstChild; + while ($node) { + if ($node->localName == 'XPath') { + $arXPath = array(); + $arXPath['query'] = '(.//. | .//@* | .//namespace::*)['.$node->nodeValue.']'; + $arXPath['namespaces'] = array(); + $nslist = $xpath->query('./namespace::*', $node); + foreach ($nslist AS $nsnode) { + if ($nsnode->localName != "xml") { + $arXPath['namespaces'][$nsnode->localName] = $nsnode->nodeValue; + } + } + break; + } + $node = $node->nextSibling; + } + break; + } + } + if ($data instanceof DOMNode) { + $data = $this->canonicalizeData($objData, $canonicalMethod, $arXPath, $prefixList); + } + return $data; + } + + /** + * @param DOMNode $refNode + * @return bool + */ + public function processRefNode($refNode) + { + $dataObject = null; + + /* + * Depending on the URI, we may not want to include comments in the result + * See: http://www.w3.org/TR/xmldsig-core/#sec-ReferenceProcessingModel + */ + $includeCommentNodes = true; + + if ($uri = $refNode->getAttribute("URI")) { + $arUrl = parse_url($uri); + if (empty($arUrl['path'])) { + if ($identifier = $arUrl['fragment']) { + + /* This reference identifies a node with the given id by using + * a URI on the form "#identifier". This should not include comments. + */ + $includeCommentNodes = false; + + $xPath = new DOMXPath($refNode->ownerDocument); + if ($this->idNS && is_array($this->idNS)) { + foreach ($this->idNS as $nspf => $ns) { + $xPath->registerNamespace($nspf, $ns); + } + } + $iDlist = '@Id="'.XPath::filterAttrValue($identifier, XPath::DOUBLE_QUOTE).'"'; + if (is_array($this->idKeys)) { + foreach ($this->idKeys as $idKey) { + $iDlist .= " or @".XPath::filterAttrName($idKey).'="'. + XPath::filterAttrValue($identifier, XPath::DOUBLE_QUOTE).'"'; + } + } + $query = '//*['.$iDlist.']'; + $dataObject = $xPath->query($query)->item(0); + } else { + $dataObject = $refNode->ownerDocument; + } + } + } else { + /* This reference identifies the root node with an empty URI. This should + * not include comments. + */ + $includeCommentNodes = false; + + $dataObject = $refNode->ownerDocument; + } + $data = $this->processTransforms($refNode, $dataObject, $includeCommentNodes); + if (!$this->validateDigest($refNode, $data)) { + return false; + } + + if ($dataObject instanceof DOMNode) { + /* Add this node to the list of validated nodes. */ + if (! empty($identifier)) { + $this->validatedNodes[$identifier] = $dataObject; + } else { + $this->validatedNodes[] = $dataObject; + } + } + + return true; + } + + /** + * @param DOMNode $refNode + * @return null + */ + public function getRefNodeID($refNode) + { + if ($uri = $refNode->getAttribute("URI")) { + $arUrl = parse_url($uri); + if (empty($arUrl['path'])) { + if ($identifier = $arUrl['fragment']) { + return $identifier; + } + } + } + return null; + } + + /** + * @return array + * @throws Exception + */ + public function getRefIDs() + { + $refids = array(); + + $xpath = $this->getXPathObj(); + $query = "./secdsig:SignedInfo[1]/secdsig:Reference"; + $nodeset = $xpath->query($query, $this->sigNode); + if ($nodeset->length == 0) { + throw new Exception("Reference nodes not found"); + } + foreach ($nodeset AS $refNode) { + $refids[] = $this->getRefNodeID($refNode); + } + return $refids; + } + + /** + * @return bool + * @throws Exception + */ + public function validateReference() + { + $docElem = $this->sigNode->ownerDocument->documentElement; + if (! $docElem->isSameNode($this->sigNode)) { + if ($this->sigNode->parentNode != null) { + $this->sigNode->parentNode->removeChild($this->sigNode); + } + } + $xpath = $this->getXPathObj(); + $query = "./secdsig:SignedInfo[1]/secdsig:Reference"; + $nodeset = $xpath->query($query, $this->sigNode); + if ($nodeset->length == 0) { + throw new Exception("Reference nodes not found"); + } + + /* Initialize/reset the list of validated nodes. */ + $this->validatedNodes = array(); + + foreach ($nodeset AS $refNode) { + if (! $this->processRefNode($refNode)) { + /* Clear the list of validated nodes. */ + $this->validatedNodes = null; + throw new Exception("Reference validation failed"); + } + } + return true; + } + + /** + * @param DOMNode $sinfoNode + * @param DOMDocument $node + * @param string $algorithm + * @param null|array $arTransforms + * @param null|array $options + */ + private function addRefInternal($sinfoNode, $node, $algorithm, $arTransforms=null, $options=null) + { + $prefix = null; + $prefix_ns = null; + $id_name = 'Id'; + $overwrite_id = true; + $force_uri = false; + + if (is_array($options)) { + $prefix = empty($options['prefix']) ? null : $options['prefix']; + $prefix_ns = empty($options['prefix_ns']) ? null : $options['prefix_ns']; + $id_name = empty($options['id_name']) ? 'Id' : $options['id_name']; + $overwrite_id = !isset($options['overwrite']) ? true : (bool) $options['overwrite']; + $force_uri = !isset($options['force_uri']) ? false : (bool) $options['force_uri']; + } + + $attname = $id_name; + if (! empty($prefix)) { + $attname = $prefix.':'.$attname; + } + + $refNode = $this->createNewSignNode('Reference'); + $sinfoNode->appendChild($refNode); + + if (! $node instanceof DOMDocument) { + $uri = null; + if (! $overwrite_id) { + $uri = $prefix_ns ? $node->getAttributeNS($prefix_ns, $id_name) : $node->getAttribute($id_name); + } + if (empty($uri)) { + $uri = self::generateGUID(); + $node->setAttributeNS($prefix_ns, $attname, $uri); + } + $refNode->setAttribute("URI", '#'.$uri); + } elseif ($force_uri) { + $refNode->setAttribute("URI", ''); + } + + $transNodes = $this->createNewSignNode('Transforms'); + $refNode->appendChild($transNodes); + + if (is_array($arTransforms)) { + foreach ($arTransforms AS $transform) { + $transNode = $this->createNewSignNode('Transform'); + $transNodes->appendChild($transNode); + if (is_array($transform) && + (! empty($transform['http://www.w3.org/TR/1999/REC-xpath-19991116'])) && + (! empty($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['query']))) { + $transNode->setAttribute('Algorithm', 'http://www.w3.org/TR/1999/REC-xpath-19991116'); + $XPathNode = $this->createNewSignNode('XPath', $transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['query']); + $transNode->appendChild($XPathNode); + if (! empty($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['namespaces'])) { + foreach ($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['namespaces'] AS $prefix => $namespace) { + $XPathNode->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:$prefix", $namespace); + } + } + } else { + $transNode->setAttribute('Algorithm', $transform); + } + } + } elseif (! empty($this->canonicalMethod)) { + $transNode = $this->createNewSignNode('Transform'); + $transNodes->appendChild($transNode); + $transNode->setAttribute('Algorithm', $this->canonicalMethod); + } + + $canonicalData = $this->processTransforms($refNode, $node); + $digValue = $this->calculateDigest($algorithm, $canonicalData); + + $digestMethod = $this->createNewSignNode('DigestMethod'); + $refNode->appendChild($digestMethod); + $digestMethod->setAttribute('Algorithm', $algorithm); + + $digestValue = $this->createNewSignNode('DigestValue', $digValue); + $refNode->appendChild($digestValue); + } + + /** + * @param DOMDocument $node + * @param string $algorithm + * @param null|array $arTransforms + * @param null|array $options + */ + public function addReference($node, $algorithm, $arTransforms=null, $options=null) + { + if ($xpath = $this->getXPathObj()) { + $query = "./secdsig:SignedInfo"; + $nodeset = $xpath->query($query, $this->sigNode); + if ($sInfo = $nodeset->item(0)) { + $this->addRefInternal($sInfo, $node, $algorithm, $arTransforms, $options); + } + } + } + + /** + * @param array $arNodes + * @param string $algorithm + * @param null|array $arTransforms + * @param null|array $options + */ + public function addReferenceList($arNodes, $algorithm, $arTransforms=null, $options=null) + { + if ($xpath = $this->getXPathObj()) { + $query = "./secdsig:SignedInfo"; + $nodeset = $xpath->query($query, $this->sigNode); + if ($sInfo = $nodeset->item(0)) { + foreach ($arNodes AS $node) { + $this->addRefInternal($sInfo, $node, $algorithm, $arTransforms, $options); + } + } + } + } + + /** + * @param DOMElement|string $data + * @param null|string $mimetype + * @param null|string $encoding + * @return DOMElement + */ + public function addObject($data, $mimetype=null, $encoding=null) + { + $objNode = $this->createNewSignNode('Object'); + $this->sigNode->appendChild($objNode); + if (! empty($mimetype)) { + $objNode->setAttribute('MimeType', $mimetype); + } + if (! empty($encoding)) { + $objNode->setAttribute('Encoding', $encoding); + } + + if ($data instanceof DOMElement) { + $newData = $this->sigNode->ownerDocument->importNode($data, true); + } else { + $newData = $this->sigNode->ownerDocument->createTextNode($data); + } + $objNode->appendChild($newData); + + return $objNode; + } + + /** + * @param null|DOMNode $node + * @return null|XMLSecurityKey + */ + public function locateKey($node=null) + { + if (empty($node)) { + $node = $this->sigNode; + } + if (! $node instanceof DOMNode) { + return null; + } + if ($doc = $node->ownerDocument) { + $xpath = new DOMXPath($doc); + $xpath->registerNamespace('secdsig', self::XMLDSIGNS); + $query = "string(./secdsig:SignedInfo/secdsig:SignatureMethod/@Algorithm)"; + $algorithm = $xpath->evaluate($query, $node); + if ($algorithm) { + try { + $objKey = new XMLSecurityKey($algorithm, array('type' => 'public')); + } catch (Exception $e) { + return null; + } + return $objKey; + } + } + return null; + } + + /** + * Returns: + * Bool when verifying HMAC_SHA1; + * Int otherwise, with following meanings: + * 1 on succesful signature verification, + * 0 when signature verification failed, + * -1 if an error occurred during processing. + * + * NOTE: be very careful when checking the int return value, because in + * PHP, -1 will be cast to True when in boolean context. Always check the + * return value in a strictly typed way, e.g. "$obj->verify(...) === 1". + * + * @param XMLSecurityKey $objKey + * @return bool|int + * @throws Exception + */ + public function verify($objKey) + { + $doc = $this->sigNode->ownerDocument; + $xpath = new DOMXPath($doc); + $xpath->registerNamespace('secdsig', self::XMLDSIGNS); + $query = "string(./secdsig:SignatureValue)"; + $sigValue = $xpath->evaluate($query, $this->sigNode); + if (empty($sigValue)) { + throw new Exception("Unable to locate SignatureValue"); + } + return $objKey->verifySignature($this->signedInfo, base64_decode($sigValue)); + } + + /** + * @param XMLSecurityKey $objKey + * @param string $data + * @return mixed|string + */ + public function signData($objKey, $data) + { + return $objKey->signData($data); + } + + /** + * @param XMLSecurityKey $objKey + * @param null|DOMNode $appendToNode + */ + public function sign($objKey, $appendToNode = null) + { + // If we have a parent node append it now so C14N properly works + if ($appendToNode != null) { + $this->resetXPathObj(); + $this->appendSignature($appendToNode); + $this->sigNode = $appendToNode->lastChild; + } + if ($xpath = $this->getXPathObj()) { + $query = "./secdsig:SignedInfo"; + $nodeset = $xpath->query($query, $this->sigNode); + if ($sInfo = $nodeset->item(0)) { + $query = "./secdsig:SignatureMethod"; + $nodeset = $xpath->query($query, $sInfo); + $sMethod = $nodeset->item(0); + $sMethod->setAttribute('Algorithm', $objKey->type); + $data = $this->canonicalizeData($sInfo, $this->canonicalMethod); + $sigValue = base64_encode($this->signData($objKey, $data)); + $sigValueNode = $this->createNewSignNode('SignatureValue', $sigValue); + if ($infoSibling = $sInfo->nextSibling) { + $infoSibling->parentNode->insertBefore($sigValueNode, $infoSibling); + } else { + $this->sigNode->appendChild($sigValueNode); + } + } + } + } + + public function appendCert() + { + + } + + /** + * @param XMLSecurityKey $objKey + * @param null|DOMNode $parent + */ + public function appendKey($objKey, $parent=null) + { + $objKey->serializeKey($parent); + } + + + /** + * This function inserts the signature element. + * + * The signature element will be appended to the element, unless $beforeNode is specified. If $beforeNode + * is specified, the signature element will be inserted as the last element before $beforeNode. + * + * @param DOMNode $node The node the signature element should be inserted into. + * @param DOMNode $beforeNode The node the signature element should be located before. + * + * @return DOMNode The signature element node + */ + public function insertSignature($node, $beforeNode = null) + { + + $document = $node->ownerDocument; + $signatureElement = $document->importNode($this->sigNode, true); + + if ($beforeNode == null) { + return $node->insertBefore($signatureElement); + } else { + return $node->insertBefore($signatureElement, $beforeNode); + } + } + + /** + * @param DOMNode $parentNode + * @param bool $insertBefore + * @return DOMNode + */ + public function appendSignature($parentNode, $insertBefore = false) + { + $beforeNode = $insertBefore ? $parentNode->firstChild : null; + return $this->insertSignature($parentNode, $beforeNode); + } + + /** + * @param string $cert + * @param bool $isPEMFormat + * @return string + */ + public static function get509XCert($cert, $isPEMFormat=true) + { + $certs = self::staticGet509XCerts($cert, $isPEMFormat); + if (! empty($certs)) { + return $certs[0]; + } + return ''; + } + + /** + * @param string $certs + * @param bool $isPEMFormat + * @return array + */ + public static function staticGet509XCerts($certs, $isPEMFormat=true) + { + if ($isPEMFormat) { + $data = ''; + $certlist = array(); + $arCert = explode("\n", $certs); + $inData = false; + foreach ($arCert AS $curData) { + if (! $inData) { + if (strncmp($curData, '-----BEGIN CERTIFICATE', 22) == 0) { + $inData = true; + } + } else { + if (strncmp($curData, '-----END CERTIFICATE', 20) == 0) { + $inData = false; + $certlist[] = $data; + $data = ''; + continue; + } + $data .= trim($curData); + } + } + return $certlist; + } else { + return array($certs); + } + } + + /** + * @param DOMElement $parentRef + * @param string $cert + * @param bool $isPEMFormat + * @param bool $isURL + * @param null|DOMXPath $xpath + * @param null|array $options + * @throws Exception + */ + public static function staticAdd509Cert($parentRef, $cert, $isPEMFormat=true, $isURL=false, $xpath=null, $options=null) + { + if ($isURL) { + $cert = file_get_contents($cert); + } + if (! $parentRef instanceof DOMElement) { + throw new Exception('Invalid parent Node parameter'); + } + $baseDoc = $parentRef->ownerDocument; + + if (empty($xpath)) { + $xpath = new DOMXPath($parentRef->ownerDocument); + $xpath->registerNamespace('secdsig', self::XMLDSIGNS); + } + + $query = "./secdsig:KeyInfo"; + $nodeset = $xpath->query($query, $parentRef); + $keyInfo = $nodeset->item(0); + $dsig_pfx = ''; + if (! $keyInfo) { + $pfx = $parentRef->lookupPrefix(self::XMLDSIGNS); + if (! empty($pfx)) { + $dsig_pfx = $pfx.":"; + } + $inserted = false; + $keyInfo = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'KeyInfo'); + + $query = "./secdsig:Object"; + $nodeset = $xpath->query($query, $parentRef); + if ($sObject = $nodeset->item(0)) { + $sObject->parentNode->insertBefore($keyInfo, $sObject); + $inserted = true; + } + + if (! $inserted) { + $parentRef->appendChild($keyInfo); + } + } else { + $pfx = $keyInfo->lookupPrefix(self::XMLDSIGNS); + if (! empty($pfx)) { + $dsig_pfx = $pfx.":"; + } + } + + // Add all certs if there are more than one + $certs = self::staticGet509XCerts($cert, $isPEMFormat); + + // Attach X509 data node + $x509DataNode = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509Data'); + $keyInfo->appendChild($x509DataNode); + + $issuerSerial = false; + $subjectName = false; + if (is_array($options)) { + if (! empty($options['issuerSerial'])) { + $issuerSerial = true; + } + if (! empty($options['subjectName'])) { + $subjectName = true; + } + } + + // Attach all certificate nodes and any additional data + foreach ($certs as $X509Cert) { + if ($issuerSerial || $subjectName) { + if ($certData = openssl_x509_parse("-----BEGIN CERTIFICATE-----\n".chunk_split($X509Cert, 64, "\n")."-----END CERTIFICATE-----\n")) { + if ($subjectName && ! empty($certData['subject'])) { + if (is_array($certData['subject'])) { + $parts = array(); + foreach ($certData['subject'] AS $key => $value) { + if (is_array($value)) { + foreach ($value as $valueElement) { + array_unshift($parts, "$key=$valueElement"); + } + } else { + array_unshift($parts, "$key=$value"); + } + } + $subjectNameValue = implode(',', $parts); + } else { + $subjectNameValue = $certData['issuer']; + } + $x509SubjectNode = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509SubjectName', $subjectNameValue); + $x509DataNode->appendChild($x509SubjectNode); + } + if ($issuerSerial && ! empty($certData['issuer']) && ! empty($certData['serialNumber'])) { + if (is_array($certData['issuer'])) { + $parts = array(); + foreach ($certData['issuer'] AS $key => $value) { + array_unshift($parts, "$key=$value"); + } + $issuerName = implode(',', $parts); + } else { + $issuerName = $certData['issuer']; + } + + $x509IssuerNode = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509IssuerSerial'); + $x509DataNode->appendChild($x509IssuerNode); + + $x509Node = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509IssuerName', $issuerName); + $x509IssuerNode->appendChild($x509Node); + $x509Node = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509SerialNumber', $certData['serialNumber']); + $x509IssuerNode->appendChild($x509Node); + } + } + + } + $x509CertNode = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509Certificate', $X509Cert); + $x509DataNode->appendChild($x509CertNode); + } + } + + /** + * @param string $cert + * @param bool $isPEMFormat + * @param bool $isURL + * @param null|array $options + */ + public function add509Cert($cert, $isPEMFormat=true, $isURL=false, $options=null) + { + if ($xpath = $this->getXPathObj()) { + self::staticAdd509Cert($this->sigNode, $cert, $isPEMFormat, $isURL, $xpath, $options); + } + } + + /** + * This function appends a node to the KeyInfo. + * + * The KeyInfo element will be created if one does not exist in the document. + * + * @param DOMNode $node The node to append to the KeyInfo. + * + * @return DOMNode The KeyInfo element node + */ + public function appendToKeyInfo($node) + { + $parentRef = $this->sigNode; + $baseDoc = $parentRef->ownerDocument; + + $xpath = $this->getXPathObj(); + if (empty($xpath)) { + $xpath = new DOMXPath($parentRef->ownerDocument); + $xpath->registerNamespace('secdsig', self::XMLDSIGNS); + } + + $query = "./secdsig:KeyInfo"; + $nodeset = $xpath->query($query, $parentRef); + $keyInfo = $nodeset->item(0); + if (! $keyInfo) { + $dsig_pfx = ''; + $pfx = $parentRef->lookupPrefix(self::XMLDSIGNS); + if (! empty($pfx)) { + $dsig_pfx = $pfx.":"; + } + $inserted = false; + $keyInfo = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'KeyInfo'); + + $query = "./secdsig:Object"; + $nodeset = $xpath->query($query, $parentRef); + if ($sObject = $nodeset->item(0)) { + $sObject->parentNode->insertBefore($keyInfo, $sObject); + $inserted = true; + } + + if (! $inserted) { + $parentRef->appendChild($keyInfo); + } + } + + $keyInfo->appendChild($node); + + return $keyInfo; + } + + /** + * This function retrieves an associative array of the validated nodes. + * + * The array will contain the id of the referenced node as the key and the node itself + * as the value. + * + * Returns: + * An associative array of validated nodes or null if no nodes have been validated. + * + * @return array Associative array of validated nodes + */ + public function getValidatedNodes() + { + return $this->validatedNodes; + } +} diff --git a/vendor/robrichards/xmlseclibs/src/XMLSecurityKey.php b/vendor/robrichards/xmlseclibs/src/XMLSecurityKey.php new file mode 100644 index 0000000000..6c01f0cc7a --- /dev/null +++ b/vendor/robrichards/xmlseclibs/src/XMLSecurityKey.php @@ -0,0 +1,749 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Robert Richards nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @author Robert Richards + * @copyright 2007-2019 Robert Richards + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + */ + +class XMLSecurityKey +{ + const TRIPLEDES_CBC = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc'; + const AES128_CBC = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc'; + const AES192_CBC = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc'; + const AES256_CBC = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc'; + const RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'; + const RSA_OAEP_MGF1P = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'; + const DSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1'; + const RSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'; + const RSA_SHA256 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'; + const RSA_SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'; + const RSA_SHA512 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'; + const HMAC_SHA1 = 'http://www.w3.org/2000/09/xmldsig#hmac-sha1'; + + /** @var array */ + private $cryptParams = array(); + + /** @var int|string */ + public $type = 0; + + /** @var mixed|null */ + public $key = null; + + /** @var string */ + public $passphrase = ""; + + /** @var string|null */ + public $iv = null; + + /** @var string|null */ + public $name = null; + + /** @var mixed|null */ + public $keyChain = null; + + /** @var bool */ + public $isEncrypted = false; + + /** @var XMLSecEnc|null */ + public $encryptedCtx = null; + + /** @var mixed|null */ + public $guid = null; + + /** + * This variable contains the certificate as a string if this key represents an X509-certificate. + * If this key doesn't represent a certificate, this will be null. + * @var string|null + */ + private $x509Certificate = null; + + /** + * This variable contains the certificate thumbprint if we have loaded an X509-certificate. + * @var string|null + */ + private $X509Thumbprint = null; + + /** + * @param string $type + * @param null|array $params + * @throws Exception + */ + public function __construct($type, $params=null) + { + switch ($type) { + case (self::TRIPLEDES_CBC): + $this->cryptParams['library'] = 'openssl'; + $this->cryptParams['cipher'] = 'des-ede3-cbc'; + $this->cryptParams['type'] = 'symmetric'; + $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc'; + $this->cryptParams['keysize'] = 24; + $this->cryptParams['blocksize'] = 8; + break; + case (self::AES128_CBC): + $this->cryptParams['library'] = 'openssl'; + $this->cryptParams['cipher'] = 'aes-128-cbc'; + $this->cryptParams['type'] = 'symmetric'; + $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc'; + $this->cryptParams['keysize'] = 16; + $this->cryptParams['blocksize'] = 16; + break; + case (self::AES192_CBC): + $this->cryptParams['library'] = 'openssl'; + $this->cryptParams['cipher'] = 'aes-192-cbc'; + $this->cryptParams['type'] = 'symmetric'; + $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc'; + $this->cryptParams['keysize'] = 24; + $this->cryptParams['blocksize'] = 16; + break; + case (self::AES256_CBC): + $this->cryptParams['library'] = 'openssl'; + $this->cryptParams['cipher'] = 'aes-256-cbc'; + $this->cryptParams['type'] = 'symmetric'; + $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc'; + $this->cryptParams['keysize'] = 32; + $this->cryptParams['blocksize'] = 16; + break; + case (self::RSA_1_5): + $this->cryptParams['library'] = 'openssl'; + $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING; + $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'; + if (is_array($params) && ! empty($params['type'])) { + if ($params['type'] == 'public' || $params['type'] == 'private') { + $this->cryptParams['type'] = $params['type']; + break; + } + } + throw new Exception('Certificate "type" (private/public) must be passed via parameters'); + case (self::RSA_OAEP_MGF1P): + $this->cryptParams['library'] = 'openssl'; + $this->cryptParams['padding'] = OPENSSL_PKCS1_OAEP_PADDING; + $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'; + $this->cryptParams['hash'] = null; + if (is_array($params) && ! empty($params['type'])) { + if ($params['type'] == 'public' || $params['type'] == 'private') { + $this->cryptParams['type'] = $params['type']; + break; + } + } + throw new Exception('Certificate "type" (private/public) must be passed via parameters'); + case (self::RSA_SHA1): + $this->cryptParams['library'] = 'openssl'; + $this->cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'; + $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING; + if (is_array($params) && ! empty($params['type'])) { + if ($params['type'] == 'public' || $params['type'] == 'private') { + $this->cryptParams['type'] = $params['type']; + break; + } + } + throw new Exception('Certificate "type" (private/public) must be passed via parameters'); + case (self::RSA_SHA256): + $this->cryptParams['library'] = 'openssl'; + $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'; + $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING; + $this->cryptParams['digest'] = 'SHA256'; + if (is_array($params) && ! empty($params['type'])) { + if ($params['type'] == 'public' || $params['type'] == 'private') { + $this->cryptParams['type'] = $params['type']; + break; + } + } + throw new Exception('Certificate "type" (private/public) must be passed via parameters'); + case (self::RSA_SHA384): + $this->cryptParams['library'] = 'openssl'; + $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'; + $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING; + $this->cryptParams['digest'] = 'SHA384'; + if (is_array($params) && ! empty($params['type'])) { + if ($params['type'] == 'public' || $params['type'] == 'private') { + $this->cryptParams['type'] = $params['type']; + break; + } + } + throw new Exception('Certificate "type" (private/public) must be passed via parameters'); + case (self::RSA_SHA512): + $this->cryptParams['library'] = 'openssl'; + $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'; + $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING; + $this->cryptParams['digest'] = 'SHA512'; + if (is_array($params) && ! empty($params['type'])) { + if ($params['type'] == 'public' || $params['type'] == 'private') { + $this->cryptParams['type'] = $params['type']; + break; + } + } + throw new Exception('Certificate "type" (private/public) must be passed via parameters'); + case (self::HMAC_SHA1): + $this->cryptParams['library'] = $type; + $this->cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#hmac-sha1'; + break; + default: + throw new Exception('Invalid Key Type'); + } + $this->type = $type; + } + + /** + * Retrieve the key size for the symmetric encryption algorithm.. + * + * If the key size is unknown, or this isn't a symmetric encryption algorithm, + * null is returned. + * + * @return int|null The number of bytes in the key. + */ + public function getSymmetricKeySize() + { + if (! isset($this->cryptParams['keysize'])) { + return null; + } + return $this->cryptParams['keysize']; + } + + /** + * Generates a session key using the openssl-extension. + * In case of using DES3-CBC the key is checked for a proper parity bits set. + * @return string + * @throws Exception + */ + public function generateSessionKey() + { + if (!isset($this->cryptParams['keysize'])) { + throw new Exception('Unknown key size for type "' . $this->type . '".'); + } + $keysize = $this->cryptParams['keysize']; + + $key = openssl_random_pseudo_bytes($keysize); + + if ($this->type === self::TRIPLEDES_CBC) { + /* Make sure that the generated key has the proper parity bits set. + * Mcrypt doesn't care about the parity bits, but others may care. + */ + for ($i = 0; $i < strlen($key); $i++) { + $byte = ord($key[$i]) & 0xfe; + $parity = 1; + for ($j = 1; $j < 8; $j++) { + $parity ^= ($byte >> $j) & 1; + } + $byte |= $parity; + $key[$i] = chr($byte); + } + } + + $this->key = $key; + return $key; + } + + /** + * Get the raw thumbprint of a certificate + * + * @param string $cert + * @return null|string + */ + public static function getRawThumbprint($cert) + { + + $arCert = explode("\n", $cert); + $data = ''; + $inData = false; + + foreach ($arCert AS $curData) { + if (! $inData) { + if (strncmp($curData, '-----BEGIN CERTIFICATE', 22) == 0) { + $inData = true; + } + } else { + if (strncmp($curData, '-----END CERTIFICATE', 20) == 0) { + break; + } + $data .= trim($curData); + } + } + + if (! empty($data)) { + return strtolower(sha1(base64_decode($data))); + } + + return null; + } + + /** + * Loads the given key, or - with isFile set true - the key from the keyfile. + * + * @param string $key + * @param bool $isFile + * @param bool $isCert + * @throws Exception + */ + public function loadKey($key, $isFile=false, $isCert = false) + { + if ($isFile) { + $this->key = file_get_contents($key); + } else { + $this->key = $key; + } + if ($isCert) { + $this->key = openssl_x509_read($this->key); + openssl_x509_export($this->key, $str_cert); + $this->x509Certificate = $str_cert; + $this->key = $str_cert; + } else { + $this->x509Certificate = null; + } + if ($this->cryptParams['library'] == 'openssl') { + switch ($this->cryptParams['type']) { + case 'public': + if ($isCert) { + /* Load the thumbprint if this is an X509 certificate. */ + $this->X509Thumbprint = self::getRawThumbprint($this->key); + } + $this->key = openssl_get_publickey($this->key); + if (! $this->key) { + throw new Exception('Unable to extract public key'); + } + break; + + case 'private': + $this->key = openssl_get_privatekey($this->key, $this->passphrase); + break; + + case'symmetric': + if (strlen($this->key) < $this->cryptParams['keysize']) { + throw new Exception('Key must contain at least 25 characters for this cipher'); + } + break; + + default: + throw new Exception('Unknown type'); + } + } + } + + /** + * ISO 10126 Padding + * + * @param string $data + * @param integer $blockSize + * @throws Exception + * @return string + */ + private function padISO10126($data, $blockSize) + { + if ($blockSize > 256) { + throw new Exception('Block size higher than 256 not allowed'); + } + $padChr = $blockSize - (strlen($data) % $blockSize); + $pattern = chr($padChr); + return $data . str_repeat($pattern, $padChr); + } + + /** + * Remove ISO 10126 Padding + * + * @param string $data + * @return string + */ + private function unpadISO10126($data) + { + $padChr = substr($data, -1); + $padLen = ord($padChr); + return substr($data, 0, -$padLen); + } + + /** + * Encrypts the given data (string) using the openssl-extension + * + * @param string $data + * @return string + */ + private function encryptSymmetric($data) + { + $this->iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->cryptParams['cipher'])); + $data = $this->padISO10126($data, $this->cryptParams['blocksize']); + $encrypted = openssl_encrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv); + if (false === $encrypted) { + throw new Exception('Failure encrypting Data (openssl symmetric) - ' . openssl_error_string()); + } + return $this->iv . $encrypted; + } + + /** + * Decrypts the given data (string) using the openssl-extension + * + * @param string $data + * @return string + */ + private function decryptSymmetric($data) + { + $iv_length = openssl_cipher_iv_length($this->cryptParams['cipher']); + $this->iv = substr($data, 0, $iv_length); + $data = substr($data, $iv_length); + $decrypted = openssl_decrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv); + if (false === $decrypted) { + throw new Exception('Failure decrypting Data (openssl symmetric) - ' . openssl_error_string()); + } + return $this->unpadISO10126($decrypted); + } + + /** + * Encrypts the given public data (string) using the openssl-extension + * + * @param string $data + * @return string + * @throws Exception + */ + private function encryptPublic($data) + { + if (! openssl_public_encrypt($data, $encrypted, $this->key, $this->cryptParams['padding'])) { + throw new Exception('Failure encrypting Data (openssl public) - ' . openssl_error_string()); + } + return $encrypted; + } + + /** + * Decrypts the given public data (string) using the openssl-extension + * + * @param string $data + * @return string + * @throws Exception + */ + private function decryptPublic($data) + { + if (! openssl_public_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) { + throw new Exception('Failure decrypting Data (openssl public) - ' . openssl_error_string()); + } + return $decrypted; + } + + /** + * Encrypts the given private data (string) using the openssl-extension + * + * @param string $data + * @return string + * @throws Exception + */ + private function encryptPrivate($data) + { + if (! openssl_private_encrypt($data, $encrypted, $this->key, $this->cryptParams['padding'])) { + throw new Exception('Failure encrypting Data (openssl private) - ' . openssl_error_string()); + } + return $encrypted; + } + + /** + * Decrypts the given private data (string) using the openssl-extension + * + * @param string $data + * @return string + * @throws Exception + */ + private function decryptPrivate($data) + { + if (! openssl_private_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) { + throw new Exception('Failure decrypting Data (openssl private) - ' . openssl_error_string()); + } + return $decrypted; + } + + /** + * Signs the given data (string) using the openssl-extension + * + * @param string $data + * @return string + * @throws Exception + */ + private function signOpenSSL($data) + { + $algo = OPENSSL_ALGO_SHA1; + if (! empty($this->cryptParams['digest'])) { + $algo = $this->cryptParams['digest']; + } + if (! openssl_sign($data, $signature, $this->key, $algo)) { + throw new Exception('Failure Signing Data: ' . openssl_error_string() . ' - ' . $algo); + } + return $signature; + } + + /** + * Verifies the given data (string) belonging to the given signature using the openssl-extension + * + * Returns: + * 1 on succesful signature verification, + * 0 when signature verification failed, + * -1 if an error occurred during processing. + * + * NOTE: be very careful when checking the return value, because in PHP, + * -1 will be cast to True when in boolean context. So always check the + * return value in a strictly typed way, e.g. "$obj->verify(...) === 1". + * + * @param string $data + * @param string $signature + * @return int + */ + private function verifyOpenSSL($data, $signature) + { + $algo = OPENSSL_ALGO_SHA1; + if (! empty($this->cryptParams['digest'])) { + $algo = $this->cryptParams['digest']; + } + return openssl_verify($data, $signature, $this->key, $algo); + } + + /** + * Encrypts the given data (string) using the regarding php-extension, depending on the library assigned to algorithm in the contructor. + * + * @param string $data + * @return mixed|string + */ + public function encryptData($data) + { + if ($this->cryptParams['library'] === 'openssl') { + switch ($this->cryptParams['type']) { + case 'symmetric': + return $this->encryptSymmetric($data); + case 'public': + return $this->encryptPublic($data); + case 'private': + return $this->encryptPrivate($data); + } + } + } + + /** + * Decrypts the given data (string) using the regarding php-extension, depending on the library assigned to algorithm in the contructor. + * + * @param string $data + * @return mixed|string + */ + public function decryptData($data) + { + if ($this->cryptParams['library'] === 'openssl') { + switch ($this->cryptParams['type']) { + case 'symmetric': + return $this->decryptSymmetric($data); + case 'public': + return $this->decryptPublic($data); + case 'private': + return $this->decryptPrivate($data); + } + } + } + + /** + * Signs the data (string) using the extension assigned to the type in the constructor. + * + * @param string $data + * @return mixed|string + */ + public function signData($data) + { + switch ($this->cryptParams['library']) { + case 'openssl': + return $this->signOpenSSL($data); + case (self::HMAC_SHA1): + return hash_hmac("sha1", $data, $this->key, true); + } + } + + /** + * Verifies the data (string) against the given signature using the extension assigned to the type in the constructor. + * + * Returns in case of openSSL: + * 1 on succesful signature verification, + * 0 when signature verification failed, + * -1 if an error occurred during processing. + * + * NOTE: be very careful when checking the return value, because in PHP, + * -1 will be cast to True when in boolean context. So always check the + * return value in a strictly typed way, e.g. "$obj->verify(...) === 1". + * + * @param string $data + * @param string $signature + * @return bool|int + */ + public function verifySignature($data, $signature) + { + switch ($this->cryptParams['library']) { + case 'openssl': + return $this->verifyOpenSSL($data, $signature); + case (self::HMAC_SHA1): + $expectedSignature = hash_hmac("sha1", $data, $this->key, true); + return strcmp($signature, $expectedSignature) == 0; + } + } + + /** + * @deprecated + * @see getAlgorithm() + * @return mixed + */ + public function getAlgorith() + { + return $this->getAlgorithm(); + } + + /** + * @return mixed + */ + public function getAlgorithm() + { + return $this->cryptParams['method']; + } + + /** + * + * @param int $type + * @param string $string + * @return null|string + */ + public static function makeAsnSegment($type, $string) + { + switch ($type) { + case 0x02: + if (ord($string) > 0x7f) + $string = chr(0).$string; + break; + case 0x03: + $string = chr(0).$string; + break; + } + + $length = strlen($string); + + if ($length < 128) { + $output = sprintf("%c%c%s", $type, $length, $string); + } else if ($length < 0x0100) { + $output = sprintf("%c%c%c%s", $type, 0x81, $length, $string); + } else if ($length < 0x010000) { + $output = sprintf("%c%c%c%c%s", $type, 0x82, $length / 0x0100, $length % 0x0100, $string); + } else { + $output = null; + } + return $output; + } + + /** + * + * Hint: Modulus and Exponent must already be base64 decoded + * @param string $modulus + * @param string $exponent + * @return string + */ + public static function convertRSA($modulus, $exponent) + { + /* make an ASN publicKeyInfo */ + $exponentEncoding = self::makeAsnSegment(0x02, $exponent); + $modulusEncoding = self::makeAsnSegment(0x02, $modulus); + $sequenceEncoding = self::makeAsnSegment(0x30, $modulusEncoding.$exponentEncoding); + $bitstringEncoding = self::makeAsnSegment(0x03, $sequenceEncoding); + $rsaAlgorithmIdentifier = pack("H*", "300D06092A864886F70D0101010500"); + $publicKeyInfo = self::makeAsnSegment(0x30, $rsaAlgorithmIdentifier.$bitstringEncoding); + + /* encode the publicKeyInfo in base64 and add PEM brackets */ + $publicKeyInfoBase64 = base64_encode($publicKeyInfo); + $encoding = "-----BEGIN PUBLIC KEY-----\n"; + $offset = 0; + while ($segment = substr($publicKeyInfoBase64, $offset, 64)) { + $encoding = $encoding.$segment."\n"; + $offset += 64; + } + return $encoding."-----END PUBLIC KEY-----\n"; + } + + /** + * @param mixed $parent + */ + public function serializeKey($parent) + { + + } + + /** + * Retrieve the X509 certificate this key represents. + * + * Will return the X509 certificate in PEM-format if this key represents + * an X509 certificate. + * + * @return string The X509 certificate or null if this key doesn't represent an X509-certificate. + */ + public function getX509Certificate() + { + return $this->x509Certificate; + } + + /** + * Get the thumbprint of this X509 certificate. + * + * Returns: + * The thumbprint as a lowercase 40-character hexadecimal number, or null + * if this isn't a X509 certificate. + * + * @return string Lowercase 40-character hexadecimal number of thumbprint + */ + public function getX509Thumbprint() + { + return $this->X509Thumbprint; + } + + + /** + * Create key from an EncryptedKey-element. + * + * @param DOMElement $element The EncryptedKey-element. + * @throws Exception + * + * @return XMLSecurityKey The new key. + */ + public static function fromEncryptedKeyElement(DOMElement $element) + { + + $objenc = new XMLSecEnc(); + $objenc->setNode($element); + if (! $objKey = $objenc->locateKey()) { + throw new Exception("Unable to locate algorithm for this Encrypted Key"); + } + $objKey->isEncrypted = true; + $objKey->encryptedCtx = $objenc; + XMLSecEnc::staticLocateKeyInfo($objKey, $element); + return $objKey; + } + +} diff --git a/vendor/robrichards/xmlseclibs/xmlseclibs.php b/vendor/robrichards/xmlseclibs/xmlseclibs.php new file mode 100644 index 0000000000..4470dab269 --- /dev/null +++ b/vendor/robrichards/xmlseclibs/xmlseclibs.php @@ -0,0 +1,47 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Robert Richards nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @author Robert Richards + * @copyright 2007-2019 Robert Richards + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version 3.0.4 + */ + +$xmlseclibs_srcdir = dirname(__FILE__) . '/src/'; +require $xmlseclibs_srcdir . '/XMLSecurityKey.php'; +require $xmlseclibs_srcdir . '/XMLSecurityDSig.php'; +require $xmlseclibs_srcdir . '/XMLSecEnc.php'; +require $xmlseclibs_srcdir . '/Utils/XPath.php';