diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..9aaba4ea --- /dev/null +++ b/.editorconfig @@ -0,0 +1,787 @@ +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +max_line_length = 120 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_formatter_off_tag = @formatter:off +ij_formatter_on_tag = @formatter:on +ij_formatter_tags_enabled = true +ij_smart_tabs = false +ij_visual_guides = +ij_wrap_on_typing = false + +[*.css] +ij_css_align_closing_brace_with_properties = false +ij_css_blank_lines_around_nested_selector = 1 +ij_css_blank_lines_between_blocks = 1 +ij_css_block_comment_add_space = false +ij_css_brace_placement = end_of_line +ij_css_enforce_quotes_on_format = false +ij_css_hex_color_long_format = false +ij_css_hex_color_lower_case = false +ij_css_hex_color_short_format = false +ij_css_hex_color_upper_case = false +ij_css_keep_blank_lines_in_code = 2 +ij_css_keep_indents_on_empty_lines = false +ij_css_keep_single_line_blocks = false +ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_css_space_after_colon = true +ij_css_space_before_opening_brace = true +ij_css_use_double_quotes = true +ij_css_value_alignment = do_not_align + +[*.sass] +indent_size = 2 +ij_sass_align_closing_brace_with_properties = false +ij_sass_blank_lines_around_nested_selector = 1 +ij_sass_blank_lines_between_blocks = 1 +ij_sass_brace_placement = 0 +ij_sass_enforce_quotes_on_format = false +ij_sass_hex_color_long_format = false +ij_sass_hex_color_lower_case = false +ij_sass_hex_color_short_format = false +ij_sass_hex_color_upper_case = false +ij_sass_keep_blank_lines_in_code = 2 +ij_sass_keep_indents_on_empty_lines = false +ij_sass_keep_single_line_blocks = false +ij_sass_line_comment_add_space = false +ij_sass_line_comment_at_first_column = false +ij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_sass_space_after_colon = true +ij_sass_space_before_opening_brace = true +ij_sass_use_double_quotes = true +ij_sass_value_alignment = 0 + +[*.scss] +indent_size = 2 +ij_scss_align_closing_brace_with_properties = false +ij_scss_blank_lines_around_nested_selector = 1 +ij_scss_blank_lines_between_blocks = 1 +ij_scss_block_comment_add_space = false +ij_scss_brace_placement = 0 +ij_scss_enforce_quotes_on_format = false +ij_scss_hex_color_long_format = false +ij_scss_hex_color_lower_case = false +ij_scss_hex_color_short_format = false +ij_scss_hex_color_upper_case = false +ij_scss_keep_blank_lines_in_code = 2 +ij_scss_keep_indents_on_empty_lines = false +ij_scss_keep_single_line_blocks = false +ij_scss_line_comment_add_space = false +ij_scss_line_comment_at_first_column = false +ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_scss_space_after_colon = true +ij_scss_space_before_opening_brace = true +ij_scss_use_double_quotes = true +ij_scss_value_alignment = 0 + +[*.vue] +indent_size = 2 +tab_width = 2 +ij_continuation_indent_size = 4 +ij_vue_indent_children_of_top_level = template +ij_vue_interpolation_new_line_after_start_delimiter = true +ij_vue_interpolation_new_line_before_end_delimiter = true +ij_vue_interpolation_wrap = off +ij_vue_keep_indents_on_empty_lines = false +ij_vue_spaces_within_interpolation_expressions = true + +[.editorconfig] +ij_editorconfig_align_group_field_declarations = false +ij_editorconfig_space_after_colon = false +ij_editorconfig_space_after_comma = true +ij_editorconfig_space_before_colon = false +ij_editorconfig_space_before_comma = false +ij_editorconfig_spaces_around_assignment_operators = true + +[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xlf,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}] +ij_xml_align_attributes = true +ij_xml_align_text = false +ij_xml_attribute_wrap = normal +ij_xml_block_comment_add_space = false +ij_xml_block_comment_at_first_column = true +ij_xml_keep_blank_lines = 2 +ij_xml_keep_indents_on_empty_lines = false +ij_xml_keep_line_breaks = true +ij_xml_keep_line_breaks_in_text = true +ij_xml_keep_whitespaces = false +ij_xml_keep_whitespaces_around_cdata = preserve +ij_xml_keep_whitespaces_inside_cdata = false +ij_xml_line_comment_at_first_column = true +ij_xml_space_after_tag_name = false +ij_xml_space_around_equals_in_attribute = false +ij_xml_space_inside_empty_tag = false +ij_xml_text_wrap = normal + +[{*.ats,*.cts,*.mts,*.ts}] +ij_continuation_indent_size = 4 +ij_typescript_align_imports = false +ij_typescript_align_multiline_array_initializer_expression = false +ij_typescript_align_multiline_binary_operation = false +ij_typescript_align_multiline_chained_methods = false +ij_typescript_align_multiline_extends_list = false +ij_typescript_align_multiline_for = true +ij_typescript_align_multiline_parameters = true +ij_typescript_align_multiline_parameters_in_calls = false +ij_typescript_align_multiline_ternary_operation = false +ij_typescript_align_object_properties = 0 +ij_typescript_align_union_types = false +ij_typescript_align_var_statements = 0 +ij_typescript_array_initializer_new_line_after_left_brace = false +ij_typescript_array_initializer_right_brace_on_new_line = false +ij_typescript_array_initializer_wrap = off +ij_typescript_assignment_wrap = off +ij_typescript_binary_operation_sign_on_next_line = false +ij_typescript_binary_operation_wrap = off +ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** +ij_typescript_blank_lines_after_imports = 1 +ij_typescript_blank_lines_around_class = 1 +ij_typescript_blank_lines_around_field = 0 +ij_typescript_blank_lines_around_field_in_interface = 0 +ij_typescript_blank_lines_around_function = 1 +ij_typescript_blank_lines_around_method = 1 +ij_typescript_blank_lines_around_method_in_interface = 1 +ij_typescript_block_brace_style = end_of_line +ij_typescript_block_comment_add_space = false +ij_typescript_block_comment_at_first_column = true +ij_typescript_call_parameters_new_line_after_left_paren = false +ij_typescript_call_parameters_right_paren_on_new_line = false +ij_typescript_call_parameters_wrap = off +ij_typescript_catch_on_new_line = false +ij_typescript_chained_call_dot_on_new_line = true +ij_typescript_class_brace_style = end_of_line +ij_typescript_comma_on_new_line = false +ij_typescript_do_while_brace_force = never +ij_typescript_else_on_new_line = false +ij_typescript_enforce_trailing_comma = keep +ij_typescript_enum_constants_wrap = on_every_item +ij_typescript_extends_keyword_wrap = off +ij_typescript_extends_list_wrap = off +ij_typescript_field_prefix = _ +ij_typescript_file_name_style = relaxed +ij_typescript_finally_on_new_line = false +ij_typescript_for_brace_force = never +ij_typescript_for_statement_new_line_after_left_paren = false +ij_typescript_for_statement_right_paren_on_new_line = false +ij_typescript_for_statement_wrap = off +ij_typescript_force_quote_style = false +ij_typescript_force_semicolon_style = false +ij_typescript_function_expression_brace_style = end_of_line +ij_typescript_if_brace_force = never +ij_typescript_import_merge_members = global +ij_typescript_import_prefer_absolute_path = global +ij_typescript_import_sort_members = true +ij_typescript_import_sort_module_name = false +ij_typescript_import_use_node_resolution = true +ij_typescript_imports_wrap = on_every_item +ij_typescript_indent_case_from_switch = true +ij_typescript_indent_chained_calls = true +ij_typescript_indent_package_children = 0 +ij_typescript_jsdoc_include_types = false +ij_typescript_jsx_attribute_value = braces +ij_typescript_keep_blank_lines_in_code = 2 +ij_typescript_keep_first_column_comment = true +ij_typescript_keep_indents_on_empty_lines = false +ij_typescript_keep_line_breaks = true +ij_typescript_keep_simple_blocks_in_one_line = false +ij_typescript_keep_simple_methods_in_one_line = false +ij_typescript_line_comment_add_space = true +ij_typescript_line_comment_at_first_column = false +ij_typescript_method_brace_style = end_of_line +ij_typescript_method_call_chain_wrap = off +ij_typescript_method_parameters_new_line_after_left_paren = false +ij_typescript_method_parameters_right_paren_on_new_line = false +ij_typescript_method_parameters_wrap = off +ij_typescript_object_literal_wrap = on_every_item +ij_typescript_object_types_wrap = on_every_item +ij_typescript_parentheses_expression_new_line_after_left_paren = false +ij_typescript_parentheses_expression_right_paren_on_new_line = false +ij_typescript_place_assignment_sign_on_next_line = false +ij_typescript_prefer_as_type_cast = false +ij_typescript_prefer_explicit_types_function_expression_returns = false +ij_typescript_prefer_explicit_types_function_returns = false +ij_typescript_prefer_explicit_types_vars_fields = false +ij_typescript_prefer_parameters_wrap = false +ij_typescript_property_prefix = +ij_typescript_reformat_c_style_comments = false +ij_typescript_space_after_colon = true +ij_typescript_space_after_comma = true +ij_typescript_space_after_dots_in_rest_parameter = false +ij_typescript_space_after_generator_mult = true +ij_typescript_space_after_property_colon = true +ij_typescript_space_after_quest = true +ij_typescript_space_after_type_colon = true +ij_typescript_space_after_unary_not = false +ij_typescript_space_before_async_arrow_lparen = true +ij_typescript_space_before_catch_keyword = true +ij_typescript_space_before_catch_left_brace = true +ij_typescript_space_before_catch_parentheses = true +ij_typescript_space_before_class_lbrace = true +ij_typescript_space_before_class_left_brace = true +ij_typescript_space_before_colon = true +ij_typescript_space_before_comma = false +ij_typescript_space_before_do_left_brace = true +ij_typescript_space_before_else_keyword = true +ij_typescript_space_before_else_left_brace = true +ij_typescript_space_before_finally_keyword = true +ij_typescript_space_before_finally_left_brace = true +ij_typescript_space_before_for_left_brace = true +ij_typescript_space_before_for_parentheses = true +ij_typescript_space_before_for_semicolon = false +ij_typescript_space_before_function_left_parenth = true +ij_typescript_space_before_generator_mult = false +ij_typescript_space_before_if_left_brace = true +ij_typescript_space_before_if_parentheses = true +ij_typescript_space_before_method_call_parentheses = false +ij_typescript_space_before_method_left_brace = true +ij_typescript_space_before_method_parentheses = false +ij_typescript_space_before_property_colon = false +ij_typescript_space_before_quest = true +ij_typescript_space_before_switch_left_brace = true +ij_typescript_space_before_switch_parentheses = true +ij_typescript_space_before_try_left_brace = true +ij_typescript_space_before_type_colon = false +ij_typescript_space_before_unary_not = false +ij_typescript_space_before_while_keyword = true +ij_typescript_space_before_while_left_brace = true +ij_typescript_space_before_while_parentheses = true +ij_typescript_spaces_around_additive_operators = true +ij_typescript_spaces_around_arrow_function_operator = true +ij_typescript_spaces_around_assignment_operators = true +ij_typescript_spaces_around_bitwise_operators = true +ij_typescript_spaces_around_equality_operators = true +ij_typescript_spaces_around_logical_operators = true +ij_typescript_spaces_around_multiplicative_operators = true +ij_typescript_spaces_around_relational_operators = true +ij_typescript_spaces_around_shift_operators = true +ij_typescript_spaces_around_unary_operator = false +ij_typescript_spaces_within_array_initializer_brackets = false +ij_typescript_spaces_within_brackets = false +ij_typescript_spaces_within_catch_parentheses = false +ij_typescript_spaces_within_for_parentheses = false +ij_typescript_spaces_within_if_parentheses = false +ij_typescript_spaces_within_imports = false +ij_typescript_spaces_within_interpolation_expressions = false +ij_typescript_spaces_within_method_call_parentheses = false +ij_typescript_spaces_within_method_parentheses = false +ij_typescript_spaces_within_object_literal_braces = false +ij_typescript_spaces_within_object_type_braces = true +ij_typescript_spaces_within_parentheses = false +ij_typescript_spaces_within_switch_parentheses = false +ij_typescript_spaces_within_type_assertion = false +ij_typescript_spaces_within_union_types = true +ij_typescript_spaces_within_while_parentheses = false +ij_typescript_special_else_if_treatment = true +ij_typescript_ternary_operation_signs_on_next_line = false +ij_typescript_ternary_operation_wrap = off +ij_typescript_union_types_wrap = on_every_item +ij_typescript_use_chained_calls_group_indents = false +ij_typescript_use_double_quotes = true +ij_typescript_use_explicit_js_extension = auto +ij_typescript_use_path_mapping = always +ij_typescript_use_public_modifier = false +ij_typescript_use_semicolon_after_statement = true +ij_typescript_var_declaration_wrap = normal +ij_typescript_while_brace_force = never +ij_typescript_while_on_new_line = false +ij_typescript_wrap_comments = false + +[{*.bash,*.sh,*.zsh}] +indent_size = 2 +tab_width = 2 +ij_shell_binary_ops_start_line = false +ij_shell_keep_column_alignment_padding = false +ij_shell_minify_program = false +ij_shell_redirect_followed_by_space = false +ij_shell_switch_cases_indented = false +ij_shell_use_unix_line_separator = true + +[{*.cjs,*.js}] +ij_continuation_indent_size = 4 +ij_javascript_align_imports = false +ij_javascript_align_multiline_array_initializer_expression = false +ij_javascript_align_multiline_binary_operation = false +ij_javascript_align_multiline_chained_methods = false +ij_javascript_align_multiline_extends_list = false +ij_javascript_align_multiline_for = true +ij_javascript_align_multiline_parameters = true +ij_javascript_align_multiline_parameters_in_calls = false +ij_javascript_align_multiline_ternary_operation = false +ij_javascript_align_object_properties = 0 +ij_javascript_align_union_types = false +ij_javascript_align_var_statements = 0 +ij_javascript_array_initializer_new_line_after_left_brace = false +ij_javascript_array_initializer_right_brace_on_new_line = false +ij_javascript_array_initializer_wrap = off +ij_javascript_assignment_wrap = off +ij_javascript_binary_operation_sign_on_next_line = false +ij_javascript_binary_operation_wrap = off +ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** +ij_javascript_blank_lines_after_imports = 1 +ij_javascript_blank_lines_around_class = 1 +ij_javascript_blank_lines_around_field = 0 +ij_javascript_blank_lines_around_function = 1 +ij_javascript_blank_lines_around_method = 1 +ij_javascript_block_brace_style = end_of_line +ij_javascript_block_comment_add_space = false +ij_javascript_block_comment_at_first_column = true +ij_javascript_call_parameters_new_line_after_left_paren = false +ij_javascript_call_parameters_right_paren_on_new_line = false +ij_javascript_call_parameters_wrap = off +ij_javascript_catch_on_new_line = false +ij_javascript_chained_call_dot_on_new_line = true +ij_javascript_class_brace_style = end_of_line +ij_javascript_comma_on_new_line = false +ij_javascript_do_while_brace_force = never +ij_javascript_else_on_new_line = false +ij_javascript_enforce_trailing_comma = keep +ij_javascript_extends_keyword_wrap = off +ij_javascript_extends_list_wrap = off +ij_javascript_field_prefix = _ +ij_javascript_file_name_style = relaxed +ij_javascript_finally_on_new_line = false +ij_javascript_for_brace_force = never +ij_javascript_for_statement_new_line_after_left_paren = false +ij_javascript_for_statement_right_paren_on_new_line = false +ij_javascript_for_statement_wrap = off +ij_javascript_force_quote_style = false +ij_javascript_force_semicolon_style = false +ij_javascript_function_expression_brace_style = end_of_line +ij_javascript_if_brace_force = never +ij_javascript_import_merge_members = global +ij_javascript_import_prefer_absolute_path = global +ij_javascript_import_sort_members = true +ij_javascript_import_sort_module_name = false +ij_javascript_import_use_node_resolution = true +ij_javascript_imports_wrap = on_every_item +ij_javascript_indent_case_from_switch = true +ij_javascript_indent_chained_calls = true +ij_javascript_indent_package_children = 0 +ij_javascript_jsx_attribute_value = braces +ij_javascript_keep_blank_lines_in_code = 2 +ij_javascript_keep_first_column_comment = true +ij_javascript_keep_indents_on_empty_lines = false +ij_javascript_keep_line_breaks = true +ij_javascript_keep_simple_blocks_in_one_line = false +ij_javascript_keep_simple_methods_in_one_line = false +ij_javascript_line_comment_add_space = true +ij_javascript_line_comment_at_first_column = false +ij_javascript_method_brace_style = end_of_line +ij_javascript_method_call_chain_wrap = off +ij_javascript_method_parameters_new_line_after_left_paren = false +ij_javascript_method_parameters_right_paren_on_new_line = false +ij_javascript_method_parameters_wrap = off +ij_javascript_object_literal_wrap = on_every_item +ij_javascript_object_types_wrap = on_every_item +ij_javascript_parentheses_expression_new_line_after_left_paren = false +ij_javascript_parentheses_expression_right_paren_on_new_line = false +ij_javascript_place_assignment_sign_on_next_line = false +ij_javascript_prefer_as_type_cast = false +ij_javascript_prefer_explicit_types_function_expression_returns = false +ij_javascript_prefer_explicit_types_function_returns = false +ij_javascript_prefer_explicit_types_vars_fields = false +ij_javascript_prefer_parameters_wrap = false +ij_javascript_property_prefix = +ij_javascript_reformat_c_style_comments = false +ij_javascript_space_after_colon = true +ij_javascript_space_after_comma = true +ij_javascript_space_after_dots_in_rest_parameter = false +ij_javascript_space_after_generator_mult = true +ij_javascript_space_after_property_colon = true +ij_javascript_space_after_quest = true +ij_javascript_space_after_type_colon = true +ij_javascript_space_after_unary_not = false +ij_javascript_space_before_async_arrow_lparen = true +ij_javascript_space_before_catch_keyword = true +ij_javascript_space_before_catch_left_brace = true +ij_javascript_space_before_catch_parentheses = true +ij_javascript_space_before_class_lbrace = true +ij_javascript_space_before_class_left_brace = true +ij_javascript_space_before_colon = true +ij_javascript_space_before_comma = false +ij_javascript_space_before_do_left_brace = true +ij_javascript_space_before_else_keyword = true +ij_javascript_space_before_else_left_brace = true +ij_javascript_space_before_finally_keyword = true +ij_javascript_space_before_finally_left_brace = true +ij_javascript_space_before_for_left_brace = true +ij_javascript_space_before_for_parentheses = true +ij_javascript_space_before_for_semicolon = false +ij_javascript_space_before_function_left_parenth = true +ij_javascript_space_before_generator_mult = false +ij_javascript_space_before_if_left_brace = true +ij_javascript_space_before_if_parentheses = true +ij_javascript_space_before_method_call_parentheses = false +ij_javascript_space_before_method_left_brace = true +ij_javascript_space_before_method_parentheses = false +ij_javascript_space_before_property_colon = false +ij_javascript_space_before_quest = true +ij_javascript_space_before_switch_left_brace = true +ij_javascript_space_before_switch_parentheses = true +ij_javascript_space_before_try_left_brace = true +ij_javascript_space_before_type_colon = false +ij_javascript_space_before_unary_not = false +ij_javascript_space_before_while_keyword = true +ij_javascript_space_before_while_left_brace = true +ij_javascript_space_before_while_parentheses = true +ij_javascript_spaces_around_additive_operators = true +ij_javascript_spaces_around_arrow_function_operator = true +ij_javascript_spaces_around_assignment_operators = true +ij_javascript_spaces_around_bitwise_operators = true +ij_javascript_spaces_around_equality_operators = true +ij_javascript_spaces_around_logical_operators = true +ij_javascript_spaces_around_multiplicative_operators = true +ij_javascript_spaces_around_relational_operators = true +ij_javascript_spaces_around_shift_operators = true +ij_javascript_spaces_around_unary_operator = false +ij_javascript_spaces_within_array_initializer_brackets = false +ij_javascript_spaces_within_brackets = false +ij_javascript_spaces_within_catch_parentheses = false +ij_javascript_spaces_within_for_parentheses = false +ij_javascript_spaces_within_if_parentheses = false +ij_javascript_spaces_within_imports = false +ij_javascript_spaces_within_interpolation_expressions = false +ij_javascript_spaces_within_method_call_parentheses = false +ij_javascript_spaces_within_method_parentheses = false +ij_javascript_spaces_within_object_literal_braces = false +ij_javascript_spaces_within_object_type_braces = true +ij_javascript_spaces_within_parentheses = false +ij_javascript_spaces_within_switch_parentheses = false +ij_javascript_spaces_within_type_assertion = false +ij_javascript_spaces_within_union_types = true +ij_javascript_spaces_within_while_parentheses = false +ij_javascript_special_else_if_treatment = true +ij_javascript_ternary_operation_signs_on_next_line = false +ij_javascript_ternary_operation_wrap = off +ij_javascript_union_types_wrap = on_every_item +ij_javascript_use_chained_calls_group_indents = false +ij_javascript_use_double_quotes = true +ij_javascript_use_explicit_js_extension = auto +ij_javascript_use_path_mapping = always +ij_javascript_use_public_modifier = false +ij_javascript_use_semicolon_after_statement = true +ij_javascript_var_declaration_wrap = normal +ij_javascript_while_brace_force = never +ij_javascript_while_on_new_line = false +ij_javascript_wrap_comments = false + +[{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml}] +ij_continuation_indent_size = 4 +ij_php_align_assignments = false +ij_php_align_class_constants = true +ij_php_align_enum_cases = true +ij_php_align_group_field_declarations = true +ij_php_align_inline_comments = false +ij_php_align_key_value_pairs = false +ij_php_align_match_arm_bodies = false +ij_php_align_multiline_array_initializer_expression = false +ij_php_align_multiline_binary_operation = true +ij_php_align_multiline_chained_methods = true +ij_php_align_multiline_extends_list = true +ij_php_align_multiline_for = true +ij_php_align_multiline_parameters = true +ij_php_align_multiline_parameters_in_calls = true +ij_php_align_multiline_ternary_operation = false +ij_php_align_named_arguments = false +ij_php_align_phpdoc_comments = false +ij_php_align_phpdoc_param_names = false +ij_php_anonymous_brace_style = end_of_line +ij_php_api_weight = 28 +ij_php_array_initializer_new_line_after_left_brace = true +ij_php_array_initializer_right_brace_on_new_line = true +ij_php_array_initializer_wrap = on_every_item +ij_php_assignment_wrap = off +ij_php_attributes_wrap = off +ij_php_author_weight = 28 +ij_php_binary_operation_sign_on_next_line = false +ij_php_binary_operation_wrap = off +ij_php_blank_lines_after_class_header = 0 +ij_php_blank_lines_after_function = 1 +ij_php_blank_lines_after_imports = 1 +ij_php_blank_lines_after_opening_tag = 1 +ij_php_blank_lines_after_package = 1 +ij_php_blank_lines_around_class = 1 +ij_php_blank_lines_around_constants = 0 +ij_php_blank_lines_around_enum_cases = 0 +ij_php_blank_lines_around_field = 0 +ij_php_blank_lines_around_method = 1 +ij_php_blank_lines_before_class_end = 0 +ij_php_blank_lines_before_imports = 1 +ij_php_blank_lines_before_method_body = 0 +ij_php_blank_lines_before_package = 1 +ij_php_blank_lines_before_return_statement = 0 +ij_php_blank_lines_between_imports = 1 +ij_php_block_brace_style = end_of_line +ij_php_call_parameters_new_line_after_left_paren = true +ij_php_call_parameters_right_paren_on_new_line = true +ij_php_call_parameters_wrap = on_every_item +ij_php_catch_on_new_line = false +ij_php_category_weight = 28 +ij_php_class_brace_style = next_line +ij_php_comma_after_last_argument = false +ij_php_comma_after_last_array_element = false +ij_php_comma_after_last_closure_use_var = false +ij_php_comma_after_last_match_arm = false +ij_php_comma_after_last_parameter = false +ij_php_concat_spaces = true +ij_php_copyright_weight = 28 +ij_php_deprecated_weight = 28 +ij_php_do_while_brace_force = always +ij_php_else_if_style = combine +ij_php_else_on_new_line = false +ij_php_example_weight = 28 +ij_php_extends_keyword_wrap = off +ij_php_extends_list_wrap = on_every_item +ij_php_fields_default_visibility = private +ij_php_filesource_weight = 28 +ij_php_finally_on_new_line = false +ij_php_for_brace_force = always +ij_php_for_statement_new_line_after_left_paren = true +ij_php_for_statement_right_paren_on_new_line = true +ij_php_for_statement_wrap = off +ij_php_force_empty_methods_in_one_line = false +ij_php_force_short_declaration_array_style = false +ij_php_getters_setters_naming_style = camel_case +ij_php_getters_setters_order_style = getters_first +ij_php_global_weight = 28 +ij_php_group_use_wrap = on_every_item +ij_php_if_brace_force = always +ij_php_if_lparen_on_next_line = false +ij_php_if_rparen_on_next_line = false +ij_php_ignore_weight = 28 +ij_php_import_sorting = alphabetic +ij_php_indent_break_from_case = true +ij_php_indent_case_from_switch = true +ij_php_indent_code_in_php_tags = false +ij_php_internal_weight = 28 +ij_php_keep_blank_lines_after_lbrace = 0 +ij_php_keep_blank_lines_before_right_brace = 0 +ij_php_keep_blank_lines_in_code = 2 +ij_php_keep_blank_lines_in_declarations = 2 +ij_php_keep_control_statement_in_one_line = true +ij_php_keep_first_column_comment = true +ij_php_keep_indents_on_empty_lines = false +ij_php_keep_line_breaks = true +ij_php_keep_rparen_and_lbrace_on_one_line = true +ij_php_keep_simple_classes_in_one_line = false +ij_php_keep_simple_methods_in_one_line = false +ij_php_lambda_brace_style = end_of_line +ij_php_license_weight = 28 +ij_php_line_comment_add_space = false +ij_php_line_comment_at_first_column = true +ij_php_link_weight = 28 +ij_php_lower_case_boolean_const = true +ij_php_lower_case_keywords = true +ij_php_lower_case_null_const = true +ij_php_method_brace_style = next_line +ij_php_method_call_chain_wrap = on_every_item +ij_php_method_parameters_new_line_after_left_paren = true +ij_php_method_parameters_right_paren_on_new_line = true +ij_php_method_parameters_wrap = on_every_item +ij_php_method_weight = 28 +ij_php_modifier_list_wrap = false +ij_php_multiline_chained_calls_semicolon_on_new_line = false +ij_php_namespace_brace_style = 1 +ij_php_new_line_after_php_opening_tag = true +ij_php_null_type_position = in_the_end +ij_php_package_weight = 28 +ij_php_param_weight = 0 +ij_php_parameters_attributes_wrap = off +ij_php_parentheses_expression_new_line_after_left_paren = false +ij_php_parentheses_expression_right_paren_on_new_line = false +ij_php_phpdoc_blank_line_before_tags = false +ij_php_phpdoc_blank_lines_around_parameters = false +ij_php_phpdoc_keep_blank_lines = true +ij_php_phpdoc_param_spaces_between_name_and_description = 1 +ij_php_phpdoc_param_spaces_between_tag_and_type = 1 +ij_php_phpdoc_param_spaces_between_type_and_name = 1 +ij_php_phpdoc_use_fqcn = false +ij_php_phpdoc_wrap_long_lines = false +ij_php_place_assignment_sign_on_next_line = false +ij_php_place_parens_for_constructor = 0 +ij_php_property_read_weight = 28 +ij_php_property_weight = 28 +ij_php_property_write_weight = 28 +ij_php_return_type_on_new_line = false +ij_php_return_weight = 1 +ij_php_see_weight = 28 +ij_php_since_weight = 28 +ij_php_sort_phpdoc_elements = true +ij_php_space_after_colon = true +ij_php_space_after_colon_in_enum_backed_type = true +ij_php_space_after_colon_in_named_argument = true +ij_php_space_after_colon_in_return_type = true +ij_php_space_after_comma = true +ij_php_space_after_for_semicolon = true +ij_php_space_after_quest = true +ij_php_space_after_type_cast = false +ij_php_space_after_unary_not = false +ij_php_space_before_array_initializer_left_brace = false +ij_php_space_before_catch_keyword = true +ij_php_space_before_catch_left_brace = true +ij_php_space_before_catch_parentheses = true +ij_php_space_before_class_left_brace = true +ij_php_space_before_closure_left_parenthesis = true +ij_php_space_before_colon = true +ij_php_space_before_colon_in_enum_backed_type = false +ij_php_space_before_colon_in_named_argument = false +ij_php_space_before_colon_in_return_type = false +ij_php_space_before_comma = false +ij_php_space_before_do_left_brace = true +ij_php_space_before_else_keyword = true +ij_php_space_before_else_left_brace = true +ij_php_space_before_finally_keyword = true +ij_php_space_before_finally_left_brace = true +ij_php_space_before_for_left_brace = true +ij_php_space_before_for_parentheses = true +ij_php_space_before_for_semicolon = false +ij_php_space_before_if_left_brace = true +ij_php_space_before_if_parentheses = true +ij_php_space_before_method_call_parentheses = false +ij_php_space_before_method_left_brace = true +ij_php_space_before_method_parentheses = false +ij_php_space_before_quest = true +ij_php_space_before_short_closure_left_parenthesis = false +ij_php_space_before_switch_left_brace = true +ij_php_space_before_switch_parentheses = true +ij_php_space_before_try_left_brace = true +ij_php_space_before_unary_not = false +ij_php_space_before_while_keyword = true +ij_php_space_before_while_left_brace = true +ij_php_space_before_while_parentheses = true +ij_php_space_between_ternary_quest_and_colon = false +ij_php_spaces_around_additive_operators = true +ij_php_spaces_around_arrow = false +ij_php_spaces_around_assignment_in_declare = false +ij_php_spaces_around_assignment_operators = true +ij_php_spaces_around_bitwise_operators = true +ij_php_spaces_around_equality_operators = true +ij_php_spaces_around_logical_operators = true +ij_php_spaces_around_multiplicative_operators = true +ij_php_spaces_around_null_coalesce_operator = true +ij_php_spaces_around_pipe_in_union_type = false +ij_php_spaces_around_relational_operators = true +ij_php_spaces_around_shift_operators = true +ij_php_spaces_around_unary_operator = false +ij_php_spaces_around_var_within_brackets = false +ij_php_spaces_within_array_initializer_braces = false +ij_php_spaces_within_brackets = false +ij_php_spaces_within_catch_parentheses = false +ij_php_spaces_within_for_parentheses = false +ij_php_spaces_within_if_parentheses = false +ij_php_spaces_within_method_call_parentheses = false +ij_php_spaces_within_method_parentheses = false +ij_php_spaces_within_parentheses = false +ij_php_spaces_within_short_echo_tags = true +ij_php_spaces_within_switch_parentheses = false +ij_php_spaces_within_while_parentheses = false +ij_php_special_else_if_treatment = false +ij_php_subpackage_weight = 28 +ij_php_ternary_operation_signs_on_next_line = false +ij_php_ternary_operation_wrap = off +ij_php_throws_weight = 2 +ij_php_todo_weight = 28 +ij_php_treat_multiline_arrays_and_lambdas_multiline = false +ij_php_unknown_tag_weight = 28 +ij_php_upper_case_boolean_const = false +ij_php_upper_case_null_const = false +ij_php_uses_weight = 28 +ij_php_var_weight = 28 +ij_php_variable_naming_style = mixed +ij_php_version_weight = 28 +ij_php_while_brace_force = always +ij_php_while_on_new_line = false + +[{*.har,*.jsb2,*.jsb3,*.json,*.lang,.babelrc,.eslintrc,.prettierrc,.stylelintrc,bowerrc,composer.lock,jest.config}] +indent_size = 2 +ij_json_array_wrapping = split_into_lines +ij_json_keep_blank_lines_in_code = 0 +ij_json_keep_indents_on_empty_lines = false +ij_json_keep_line_breaks = true +ij_json_keep_trailing_comma = false +ij_json_object_wrapping = split_into_lines +ij_json_property_alignment = do_not_align +ij_json_space_after_colon = true +ij_json_space_after_comma = true +ij_json_space_before_colon = false +ij_json_space_before_comma = false +ij_json_spaces_within_braces = false +ij_json_spaces_within_brackets = false +ij_json_wrap_long_lines = false + +[{*.htm,*.html,*.sht,*.shtm,*.shtml}] +ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3 +ij_html_align_attributes = true +ij_html_align_text = false +ij_html_attribute_wrap = normal +ij_html_block_comment_add_space = false +ij_html_block_comment_at_first_column = true +ij_html_do_not_align_children_of_min_lines = 0 +ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p +ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot +ij_html_enforce_quotes = false +ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var +ij_html_keep_blank_lines = 2 +ij_html_keep_indents_on_empty_lines = false +ij_html_keep_line_breaks = true +ij_html_keep_line_breaks_in_text = true +ij_html_keep_whitespaces = false +ij_html_keep_whitespaces_inside = span,pre,textarea +ij_html_line_comment_at_first_column = true +ij_html_new_line_after_last_attribute = never +ij_html_new_line_before_first_attribute = never +ij_html_quote_style = double +ij_html_remove_new_line_before_tags = br +ij_html_space_after_tag_name = false +ij_html_space_around_equality_in_attribute = false +ij_html_space_inside_empty_tag = false +ij_html_text_wrap = normal + +[{*.http,*.rest}] +indent_size = 0 +ij_continuation_indent_size = 4 +ij_http request_call_parameters_wrap = normal +ij_http request_method_parameters_wrap = split_into_lines +ij_http request_space_before_comma = true +ij_http request_spaces_around_assignment_operators = true + +[{*.markdown,*.md}] +ij_markdown_force_one_space_after_blockquote_symbol = true +ij_markdown_force_one_space_after_header_symbol = true +ij_markdown_force_one_space_after_list_bullet = true +ij_markdown_force_one_space_between_words = true +ij_markdown_format_tables = true +ij_markdown_insert_quote_arrows_on_wrap = true +ij_markdown_keep_indents_on_empty_lines = false +ij_markdown_keep_line_breaks_inside_text_blocks = true +ij_markdown_max_lines_around_block_elements = 1 +ij_markdown_max_lines_around_header = 1 +ij_markdown_max_lines_between_paragraphs = 1 +ij_markdown_min_lines_around_block_elements = 1 +ij_markdown_min_lines_around_header = 1 +ij_markdown_min_lines_between_paragraphs = 1 +ij_markdown_wrap_text_if_long = true +ij_markdown_wrap_text_inside_blockquotes = true + +[{*.twig,*.volt}] +ij_twig_keep_indents_on_empty_lines = false +ij_twig_spaces_inside_comments_delimiters = true +ij_twig_spaces_inside_delimiters = true +ij_twig_spaces_inside_variable_delimiters = true + +[{*.yaml,*.yml}] +indent_size = 2 +ij_yaml_align_values_properties = do_not_align +ij_yaml_autoinsert_sequence_marker = true +ij_yaml_block_mapping_on_new_line = false +ij_yaml_indent_sequence_value = true +ij_yaml_keep_indents_on_empty_lines = false +ij_yaml_keep_line_breaks = true +ij_yaml_sequence_on_new_line = false +ij_yaml_space_before_colon = false +ij_yaml_spaces_within_braces = true +ij_yaml_spaces_within_brackets = true diff --git a/app/modules/web/Controllers/ConfigLdap/CheckController.php b/app/modules/web/Controllers/ConfigLdap/CheckController.php index 60223ede..05e512bb 100644 --- a/app/modules/web/Controllers/ConfigLdap/CheckController.php +++ b/app/modules/web/Controllers/ConfigLdap/CheckController.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -26,11 +26,14 @@ namespace SP\Modules\Web\Controllers\ConfigLdap; use Exception; +use JsonException; use SP\Core\Acl\ActionsInterface; use SP\Core\Acl\UnauthorizedPageException; use SP\Core\Application; use SP\Core\Events\Event; use SP\Core\Exceptions\CheckException; +use SP\Core\Exceptions\SessionTimeout; +use SP\Core\Exceptions\SPException; use SP\Domain\Auth\Ports\LdapCheckServiceInterface; use SP\Http\JsonResponse; use SP\Modules\Web\Controllers\SimpleControllerBase; @@ -38,31 +41,31 @@ use SP\Modules\Web\Controllers\Traits\JsonTrait; use SP\Mvc\Controller\SimpleControllerHelper; use SP\Mvc\View\TemplateInterface; +use function SP\__; +use function SP\__u; +use function SP\processException; + /** * Class CheckController */ final class CheckController extends SimpleControllerBase { - use JsonTrait, ConfigLdapTrait; - - private LdapCheckServiceInterface $ldapCheckService; - private TemplateInterface $template; + use ConfigLdapTrait; + use JsonTrait; public function __construct( - Application $application, - SimpleControllerHelper $simpleControllerHelper, - LdapCheckServiceInterface $ldapCheckService, - TemplateInterface $template + Application $application, + SimpleControllerHelper $simpleControllerHelper, + private readonly LdapCheckServiceInterface $ldapCheckService, + private readonly TemplateInterface $template ) { parent::__construct($application, $simpleControllerHelper); - - $this->ldapCheckService = $ldapCheckService; - $this->template = $template; } /** * @return bool - * @throws \JsonException + * @throws JsonException + * @throws SPException */ public function checkAction(): bool { @@ -80,9 +83,7 @@ final class CheckController extends SimpleControllerBase ); } - $this->ldapCheckService->checkConnection($ldapParams); - - $data = $this->ldapCheckService->getObjects(false); + $data = $this->ldapCheckService->getObjects(false, $ldapParams); $this->template->addTemplate('results', 'itemshow'); $this->template->assign('header', __('Results')); @@ -104,8 +105,8 @@ final class CheckController extends SimpleControllerBase /** * @return void - * @throws \JsonException - * @throws \SP\Core\Exceptions\SessionTimeout + * @throws SessionTimeout + * @throws SPException */ protected function initialize(): void { diff --git a/app/modules/web/Controllers/ConfigLdap/CheckImportController.php b/app/modules/web/Controllers/ConfigLdap/CheckImportController.php index 1d8dd947..114759a0 100644 --- a/app/modules/web/Controllers/ConfigLdap/CheckImportController.php +++ b/app/modules/web/Controllers/ConfigLdap/CheckImportController.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -26,51 +26,45 @@ namespace SP\Modules\Web\Controllers\ConfigLdap; use Exception; -use Klein\Klein; -use SP\Core\Acl\Acl; use SP\Core\Acl\ActionsInterface; use SP\Core\Acl\UnauthorizedPageException; use SP\Core\Application; use SP\Core\Events\Event; use SP\Core\Exceptions\CheckException; -use SP\Core\PhpExtensionChecker; -use SP\Core\UI\ThemeInterface; +use SP\Core\Exceptions\SessionTimeout; +use SP\Core\Exceptions\SPException; use SP\Domain\Auth\Ports\LdapCheckServiceInterface; use SP\Http\JsonResponse; -use SP\Http\RequestInterface; use SP\Modules\Web\Controllers\SimpleControllerBase; use SP\Modules\Web\Controllers\Traits\JsonTrait; +use SP\Mvc\Controller\SimpleControllerHelper; use SP\Mvc\View\TemplateInterface; +use function SP\__; +use function SP\__u; +use function SP\processException; + /** * Class CheckImportController */ final class CheckImportController extends SimpleControllerBase { - use JsonTrait, ConfigLdapTrait; - - private LdapCheckServiceInterface $ldapCheckService; - private TemplateInterface $template; + use JsonTrait; + use ConfigLdapTrait; public function __construct( - Application $application, - ThemeInterface $theme, - Klein $router, - Acl $acl, - RequestInterface $request, - PhpExtensionChecker $extensionChecker, - LdapCheckServiceInterface $ldapCheckService, - TemplateInterface $template + Application $application, + SimpleControllerHelper $simpleControllerHelper, + private readonly LdapCheckServiceInterface $ldapCheckService, + private readonly TemplateInterface $template ) { - parent::__construct($application, $theme); - - $this->ldapCheckService = $ldapCheckService; - $this->template = $template; + parent::__construct($application, $simpleControllerHelper); } /** * @return bool * @throws \JsonException + * @throws SPException */ public function checkImportAction(): bool { @@ -85,14 +79,15 @@ final class CheckImportController extends SimpleControllerBase return $this->returnJsonResponse(JsonResponse::JSON_ERROR, __u('Missing LDAP parameters')); } - $this->ldapCheckService->checkConnection($ldapParams); - $filter = $this->request->analyzeString('ldap_import_filter'); if (empty($filter)) { - $data = $this->ldapCheckService->getObjects($this->request->analyzeBool('ldap_import_groups', false)); + $data = $this->ldapCheckService->getObjects( + $this->request->analyzeBool('ldap_import_groups', false), + $ldapParams + ); } else { - $data = $this->ldapCheckService->getObjectsByFilter($filter); + $data = $this->ldapCheckService->getObjectsByFilter($filter, $ldapParams); } $this->template->addTemplate('results', 'itemshow'); @@ -116,8 +111,8 @@ final class CheckImportController extends SimpleControllerBase /** * @return void - * @throws \JsonException - * @throws \SP\Core\Exceptions\SessionTimeout + * @throws SPException + * @throws SessionTimeout */ protected function initialize(): void { diff --git a/app/modules/web/Controllers/ConfigLdap/ConfigLdapTrait.php b/app/modules/web/Controllers/ConfigLdap/ConfigLdapTrait.php index aa37fccb..ce4b9e16 100644 --- a/app/modules/web/Controllers/ConfigLdap/ConfigLdapTrait.php +++ b/app/modules/web/Controllers/ConfigLdap/ConfigLdapTrait.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -24,11 +24,12 @@ namespace SP\Modules\Web\Controllers\ConfigLdap; - use SP\Core\Exceptions\ValidationException; use SP\Http\RequestInterface; use SP\Providers\Auth\Ldap\LdapParams; -use SP\Providers\Auth\Ldap\LdapTypeInterface; +use SP\Providers\Auth\Ldap\LdapTypeEnum; + +use function SP\__u; /** * Trait ConfigLdapTrait @@ -36,10 +37,10 @@ use SP\Providers\Auth\Ldap\LdapTypeInterface; trait ConfigLdapTrait { /** - * @param \SP\Http\RequestInterface $request + * @param RequestInterface $request * * @return LdapParams - * @throws \SP\Core\Exceptions\ValidationException + * @throws ValidationException */ protected function getLdapParamsFromRequest(RequestInterface $request): LdapParams { @@ -49,20 +50,24 @@ trait ConfigLdapTrait throw new ValidationException(__u('Wrong LDAP parameters')); } - $params = new LdapParams(); - $params->setServer($data['server']); + $type = LdapTypeEnum::tryFrom($request->analyzeInt('ldap_server_type')) ?: LdapTypeEnum::STD; + + $params = new LdapParams( + $data['server'], + $type, + $request->analyzeString('ldap_binduser'), + $request->analyzeEncrypted('ldap_bindpass') + ); + $params->setPort($data['port'] ?? 389); $params->setSearchBase($request->analyzeString('ldap_base')); $params->setGroup($request->analyzeString('ldap_group')); - $params->setBindDn($request->analyzeString('ldap_binduser')); - $params->setBindPass($request->analyzeEncrypted('ldap_bindpass')); - $params->setType($request->analyzeInt('ldap_server_type', LdapTypeInterface::LDAP_STD)); $params->setTlsEnabled($request->analyzeBool('ldap_tls_enabled', false)); - $params->setFilterUserObject($request->analyzeString('ldap_filter_user_object', null)); - $params->setFilterGroupObject($request->analyzeString('ldap_filter_group_object', null)); + $params->setFilterUserObject($request->analyzeString('ldap_filter_user_object')); + $params->setFilterGroupObject($request->analyzeString('ldap_filter_group_object')); $params->setFilterUserAttributes($request->analyzeArray('ldap_filter_user_attributes')); $params->setFilterGroupAttributes($request->analyzeArray('ldap_filter_group_attributes')); return $params; } -} \ No newline at end of file +} diff --git a/app/modules/web/Controllers/ConfigManager/IndexController.php b/app/modules/web/Controllers/ConfigManager/IndexController.php index 128dc482..dabe3342 100644 --- a/app/modules/web/Controllers/ConfigManager/IndexController.php +++ b/app/modules/web/Controllers/ConfigManager/IndexController.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -61,7 +61,7 @@ use SP\Mvc\View\Components\SelectItemAdapter; use SP\Plugin\PluginManager; use SP\Providers\Auth\Ldap\LdapMsAds; use SP\Providers\Auth\Ldap\LdapStd; -use SP\Providers\Auth\Ldap\LdapTypeInterface; +use SP\Providers\Auth\Ldap\LdapTypeEnum; use SP\Providers\Log\LogInterface; use SP\Providers\Mail\MailHandler; use SP\Util\Util; @@ -82,17 +82,17 @@ final class IndexController extends ControllerBase private PluginManager $pluginManager; public function __construct( - Application $application, - WebControllerHelper $webControllerHelper, - TabsHelper $tabsHelper, - UserServiceInterface $userService, - UserGroupServiceInterface $userGroupService, + Application $application, + WebControllerHelper $webControllerHelper, + TabsHelper $tabsHelper, + UserServiceInterface $userService, + UserGroupServiceInterface $userGroupService, UserProfileServiceInterface $userProfileService, - MimeTypesInterface $mimeTypes, - DatabaseUtil $databaseUtil, - ConfigServiceInterface $configService, - AccountServiceInterface $accountService, - PluginManager $pluginManager + MimeTypesInterface $mimeTypes, + DatabaseUtil $databaseUtil, + ConfigServiceInterface $configService, + AccountServiceInterface $accountService, + PluginManager $pluginManager ) { parent::__construct($application, $webControllerHelper); @@ -235,10 +235,10 @@ final class IndexController extends ControllerBase $template->assign( 'logEvents', SelectItemAdapter::factory($events) - ->getItemsFromArraySelected( - $this->configData->getLogEvents(), - true - ) + ->getItemsFromArraySelected( + $this->configData->getLogEvents(), + true + ) ); return new DataTab(__('General'), $template); @@ -319,14 +319,14 @@ final class IndexController extends ControllerBase ); $serverTypes = [ - LdapTypeInterface::LDAP_STD => 'Standard', - LdapTypeInterface::LDAP_ADS => 'Active Directory', + LdapTypeEnum::STD->value => 'Standard', + LdapTypeEnum::ADS->value => 'Active Directory', ]; $template->assign( 'serverTypes', SelectItemAdapter::factory($serverTypes) - ->getItemsFromArraySelected([$this->configData->getLdapType()]) + ->getItemsFromArraySelected([$this->configData->getLdapType()]) ); $userAttributes = array_merge( @@ -338,7 +338,7 @@ final class IndexController extends ControllerBase $template->assign( 'userAttributes', SelectItemAdapter::factory($userAttributes) - ->getItemsFromArraySelected($this->configData->getLdapFilterUserAttributes()) + ->getItemsFromArraySelected($this->configData->getLdapFilterUserAttributes()) ); $groupAttributes = array_merge( @@ -350,7 +350,7 @@ final class IndexController extends ControllerBase $template->assign( 'groupAttributes', SelectItemAdapter::factory($groupAttributes) - ->getItemsFromArraySelected($this->configData->getLdapFilterGroupAttributes()) + ->getItemsFromArraySelected($this->configData->getLdapFilterGroupAttributes()) ); return new DataTab(__('LDAP'), $template); @@ -386,10 +386,10 @@ final class IndexController extends ControllerBase $template->assign( 'mailEvents', SelectItemAdapter::factory($events) - ->getItemsFromArraySelected( - $mailEvents, - true - ) + ->getItemsFromArraySelected( + $mailEvents, + true + ) ); return new DataTab(__('Mail'), $template); @@ -534,12 +534,12 @@ final class IndexController extends ControllerBase $template->assign( 'userGroups', SelectItemAdapter::factory($this->userGroupService->getAllBasic()) - ->getItemsFromModelSelected([$this->userData->getUserGroupId()]) + ->getItemsFromModelSelected([$this->userData->getUserGroupId()]) ); $template->assign( 'users', SelectItemAdapter::factory($this->userService->getAllBasic()) - ->getItemsFromModelSelected([$this->userData->getId()]) + ->getItemsFromModelSelected([$this->userData->getId()]) ); return new DataTab(__('Import Accounts'), $template); @@ -557,7 +557,7 @@ final class IndexController extends ControllerBase $template->addTemplate('info'); $template->assign('dbInfo', $this->databaseUtil->getDBinfo()); - $template->assign('dbName', $this->configData->getDbName().'@'.$this->configData->getDbHost()); + $template->assign('dbName', $this->configData->getDbName() . '@' . $this->configData->getDbHost()); $template->assign( 'configBackupDate', date('r', $this->configService->getByParam('config_backup_date', 0)) diff --git a/composer.json b/composer.json index da3b02a9..c33c3b8d 100644 --- a/composer.json +++ b/composer.json @@ -54,7 +54,8 @@ "league/fractal": "^0.20", "symfony/console": "^v5.1", "symfony/lock": "^v5.0", - "aura/sqlquery": "~3.0" + "aura/sqlquery": "~3.0", + "laminas/laminas-ldap": "^2.17" }, "require-dev": { "roave/security-advisories": "dev-latest", diff --git a/composer.lock b/composer.lock index 48002dfc..558ca2ed 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "bba9096278509c2d1c21cb52101586bc", + "content-hash": "321f22ee1b6ccb2d6740fe5a18907cc6", "packages": [ { "name": "ademarre/binary-to-text-php", @@ -1139,16 +1139,16 @@ }, { "name": "guzzlehttp/promises", - "version": "1.5.2", + "version": "1.5.3", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "b94b2807d85443f9719887892882d0329d1e2598" + "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598", - "reference": "b94b2807d85443f9719887892882d0329d1e2598", + "url": "https://api.github.com/repos/guzzle/promises/zipball/67ab6e18aaa14d753cc148911d273f6e6cb6721e", + "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e", "shasum": "" }, "require": { @@ -1158,11 +1158,6 @@ "symfony/phpunit-bridge": "^4.4 || ^5.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5-dev" - } - }, "autoload": { "files": [ "src/functions_include.php" @@ -1203,7 +1198,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.2" + "source": "https://github.com/guzzle/promises/tree/1.5.3" }, "funding": [ { @@ -1219,7 +1214,7 @@ "type": "tidelift" } ], - "time": "2022-08-28T14:55:35+00:00" + "time": "2023-05-21T12:31:43+00:00" }, { "name": "guzzlehttp/psr7", @@ -1386,6 +1381,72 @@ }, "time": "2017-02-01T23:08:58+00:00" }, + { + "name": "laminas/laminas-ldap", + "version": "2.17.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-ldap.git", + "reference": "cb66c477ec2fd2a7f38527c59d0635a9166b6597" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-ldap/zipball/cb66c477ec2fd2a7f38527c59d0635a9166b6597", + "reference": "cb66c477ec2fd2a7f38527c59d0635a9166b6597", + "shasum": "" + }, + "require": { + "ext-ldap": "*", + "php": "~8.0.0 || ~8.1.0 || ~8.2.0" + }, + "conflict": { + "zendframework/zend-ldap": "*" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~2.4.0", + "laminas/laminas-config": "^3.8.0", + "laminas/laminas-eventmanager": "^3.6.0", + "laminas/laminas-stdlib": "^3.15.0", + "php-mock/php-mock-phpunit": "^2.6.1", + "phpunit/phpunit": "^9.5.26", + "psalm/plugin-phpunit": "^0.18.0", + "vimeo/psalm": "^5.0.0" + }, + "suggest": { + "laminas/laminas-eventmanager": "Laminas\\EventManager component" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Ldap\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Provides support for LDAP operations including but not limited to binding, searching and modifying entries in an LDAP directory", + "homepage": "https://laminas.dev", + "keywords": [ + "laminas", + "ldap" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-ldap/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-ldap/issues", + "rss": "https://github.com/laminas/laminas-ldap/releases.atom", + "source": "https://github.com/laminas/laminas-ldap" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2022-12-19T19:50:51+00:00" + }, { "name": "laravel/serializable-closure", "version": "v1.3.0", @@ -3855,16 +3916,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.1", + "version": "10.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "884a0da7f9f46f28b2cb69134217fd810b793974" + "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/884a0da7f9f46f28b2cb69134217fd810b793974", - "reference": "884a0da7f9f46f28b2cb69134217fd810b793974", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/db1497ec8dd382e82c962f7abbe0320e4882ee4e", + "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e", "shasum": "" }, "require": { @@ -3921,7 +3982,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.1" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.2" }, "funding": [ { @@ -3929,7 +3990,7 @@ "type": "github" } ], - "time": "2023-04-17T12:15:40+00:00" + "time": "2023-05-22T09:04:27+00:00" }, { "name": "phpunit/php-file-iterator", @@ -4280,12 +4341,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "67504d48750a345d427a6e653b46704599c15d3d" + "reference": "2d9146bd69addfbd99652930a825c8ba7b8ea13c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/67504d48750a345d427a6e653b46704599c15d3d", - "reference": "67504d48750a345d427a6e653b46704599c15d3d", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/2d9146bd69addfbd99652930a825c8ba7b8ea13c", + "reference": "2d9146bd69addfbd99652930a825c8ba7b8ea13c", "shasum": "" }, "conflict": { @@ -4349,7 +4410,7 @@ "cockpit-hq/cockpit": "<2.4.1", "codeception/codeception": "<3.1.3|>=4,<4.1.22", "codeigniter/framework": "<=3.0.6", - "codeigniter4/framework": "<4.2.11", + "codeigniter4/framework": "<4.3.5", "codeigniter4/shield": "<1-beta.4|= 1.0.0-beta", "codiad/codiad": "<=2.8.4", "composer/composer": "<1.10.26|>=2-alpha.1,<2.2.12|>=2.3,<2.3.5", @@ -4361,7 +4422,7 @@ "contao/core-bundle": "<4.9.40|>=4.10,<4.11.7|>=4.13,<4.13.21|>=5.1,<5.1.4|= 4.10.0", "contao/listing-bundle": ">=4,<4.4.8", "contao/managed-edition": "<=1.5", - "craftcms/cms": "<=3.8.3|>=4,<=4.4.3|>= 4.0.0-RC1, < 4.3.7|>= 4.0.0-RC1, < 4.2.1", + "craftcms/cms": "<=3.8.3|>=4,<4.4.6|>= 4.0.0-RC1, < 4.3.7|>= 4.0.0-RC1, < 4.2.1", "croogo/croogo": "<3.0.7", "cuyz/valinor": "<0.12", "czproject/git-php": "<4.0.3", @@ -4683,7 +4744,7 @@ "simplesamlphp/simplesamlphp-module-openidprovider": "<0.9", "simplito/elliptic-php": "<1.0.6", "sitegeist/fluid-components": "<3.5", - "slim/psr7": "<1.6.1", + "slim/psr7": "<1.4.1|>=1.5,<1.5.1|>=1.6,<1.6.1", "slim/slim": "<2.6", "smarty/smarty": "<3.1.48|>=4,<4.3.1", "snipe/snipe-it": "<=6.0.14|>= 6.0.0-RC-1, <= 6.0.0-RC-5", @@ -4880,7 +4941,7 @@ "type": "tidelift" } ], - "time": "2023-05-20T00:13:18+00:00" + "time": "2023-05-22T21:04:18+00:00" }, { "name": "sebastian/cli-parser", diff --git a/lib/SP/Core/Definitions/CoreDefinitions.php b/lib/SP/Core/Definitions/CoreDefinitions.php index d07f0671..80b12d6d 100644 --- a/lib/SP/Core/Definitions/CoreDefinitions.php +++ b/lib/SP/Core/Definitions/CoreDefinitions.php @@ -49,6 +49,9 @@ use SP\Core\MimeTypesInterface; use SP\Core\ProvidersHelper; use SP\Core\UI\Theme; use SP\Core\UI\ThemeInterface; +use SP\Domain\Auth\Ports\LdapActionsInterface; +use SP\Domain\Auth\Ports\LdapAuthInterface; +use SP\Domain\Auth\Ports\LdapConnectionInterface; use SP\Domain\Config\Ports\ConfigDataInterface; use SP\Domain\Config\Ports\ConfigInterface; use SP\Domain\Config\Services\ConfigBackupService; @@ -79,9 +82,10 @@ use SP\Providers\Auth\Browser\BrowserAuth; use SP\Providers\Auth\Browser\BrowserAuthInterface; use SP\Providers\Auth\Database\DatabaseAuth; use SP\Providers\Auth\Database\DatabaseAuthInterface; -use SP\Providers\Auth\Ldap\Ldap; +use SP\Providers\Auth\Ldap\LdapActions; use SP\Providers\Auth\Ldap\LdapAuth; -use SP\Providers\Auth\Ldap\LdapAuthInterface; +use SP\Providers\Auth\Ldap\LdapBase; +use SP\Providers\Auth\Ldap\LdapConnection; use SP\Providers\Auth\Ldap\LdapParams; use SP\Providers\Log\DatabaseLogHandler; use SP\Providers\Log\FileLogHandler; @@ -91,6 +95,7 @@ use SP\Providers\Mail\MailHandler; use SP\Providers\Mail\MailProvider; use SP\Providers\Mail\PhpMailerWrapper; use SP\Providers\Notification\NotificationHandler; + use function DI\autowire; use function DI\create; use function DI\factory; @@ -105,11 +110,11 @@ final class CoreDefinitions public static function getDefinitions(): array { return [ - RequestInterface::class => create(Request::class) + RequestInterface::class => create(Request::class) ->constructor(\Klein\Request::createFromGlobals(), autowire(CryptPKI::class)), - ContextInterface::class => + ContextInterface::class => static fn() => ContextFactory::getForModule(APP_MODULE), - ConfigInterface::class => create(ConfigFileService::class) + ConfigInterface::class => create(ConfigFileService::class) ->constructor( create(XmlHandler::class) ->constructor(create(FileHandler::class)->constructor(CONFIG_FILE)), @@ -117,37 +122,40 @@ final class CoreDefinitions get(ContextInterface::class), autowire(ConfigBackupService::class) ), - ConfigDataInterface::class => + ConfigDataInterface::class => static fn(ConfigInterface $config) => $config->getConfigData(), - DatabaseConnectionData::class => factory([DatabaseConnectionData::class, 'getFromConfig']), - DbStorageInterface::class => autowire(MysqlHandler::class), - Actions::class => + DatabaseConnectionData::class => factory([DatabaseConnectionData::class, 'getFromConfig']), + DbStorageInterface::class => autowire(MysqlHandler::class), + Actions::class => static fn() => new Actions( new FileCache(Actions::ACTIONS_CACHE_FILE), new XmlHandler(new FileHandler(ACTIONS_FILE)) ), - MimeTypesInterface::class => + MimeTypesInterface::class => static fn() => new MimeTypes( new FileCache(MimeTypes::MIME_CACHE_FILE), new XmlHandler(new FileHandler(MIMETYPES_FILE)) ), - Acl::class => autowire(Acl::class) + Acl::class => autowire(Acl::class) ->constructorParameter('actions', get(Actions::class)), - ThemeInterface::class => autowire(Theme::class) + ThemeInterface::class => autowire(Theme::class) ->constructorParameter('module', APP_MODULE) ->constructorParameter( 'fileCache', create(FileCache::class)->constructor(Theme::ICONS_CACHE_FILE) ), - TemplateInterface::class => autowire(Template::class), - DatabaseAuthInterface::class => autowire(DatabaseAuth::class), - BrowserAuthInterface::class => autowire(BrowserAuth::class), - LdapAuthInterface::class => autowire(LdapAuth::class) + TemplateInterface::class => autowire(Template::class), + DatabaseAuthInterface::class => autowire(DatabaseAuth::class), + BrowserAuthInterface::class => autowire(BrowserAuth::class), + LdapParams::class => factory([LdapParams::class, 'getFrom']), + LdapConnectionInterface::class => autowire(LdapConnection::class), + LdapActionsInterface::class => autowire(LdapActions::class), + LdapAuthInterface::class => autowire(LdapAuth::class) ->constructorParameter( 'ldap', - factory([Ldap::class, 'factory'])->parameter('ldapParams', factory([LdapParams::class, 'getFrom'])) + factory([LdapBase::class, 'factory']) ), - AuthProviderInterface::class => + AuthProviderInterface::class => static function (ContainerInterface $c, ConfigDataInterface $configData) { $provider = $c->get(AuthProvider::class); @@ -161,18 +169,18 @@ final class CoreDefinitions return $provider; }, - Logger::class => create(Logger::class) + Logger::class => create(Logger::class) ->constructor('syspass'), - \GuzzleHttp\Client::class => create(\GuzzleHttp\Client::class) + \GuzzleHttp\Client::class => create(\GuzzleHttp\Client::class) ->constructor(factory([Client::class, 'getOptions'])), - CSRF::class => autowire(CSRF::class), - LanguageInterface::class => autowire(Language::class), - DatabaseInterface::class => autowire(Database::class), - MailProviderInterface::class => autowire(MailProvider::class), - MailerInterface::class => autowire(PhpMailerWrapper::class)->constructor( + CSRF::class => autowire(CSRF::class), + LanguageInterface::class => autowire(Language::class), + DatabaseInterface::class => autowire(Database::class), + MailProviderInterface::class => autowire(MailProvider::class), + MailerInterface::class => autowire(PhpMailerWrapper::class)->constructor( create(PHPMailer::class)->constructor(true) ), - DatabaseSetupInterface::class => static function (RequestInterface $request) { + DatabaseSetupInterface::class => static function (RequestInterface $request) { $installData = InstallDataFactory::buildFromRequest($request); if ($installData->getBackendType() === 'mysql') { @@ -181,7 +189,7 @@ final class CoreDefinitions throw new SPException(__u('Unimplemented'), SPException::ERROR, __u('Wrong backend type')); }, - ProvidersHelper::class => factory(static function (ContainerInterface $c) { + ProvidersHelper::class => factory(static function (ContainerInterface $c) { $configData = $c->get(ConfigDataInterface::class); if (!$configData->isInstalled()) { @@ -198,15 +206,15 @@ final class CoreDefinitions $c->get(NotificationHandler::class) ); }), - QueryFactory::class => create(QueryFactory::class) + QueryFactory::class => create(QueryFactory::class) ->constructor('mysql', QueryFactory::COMMON), - CryptInterface::class => create(Crypt::class), - CryptPKIInterface::class => autowire(CryptPKI::class) + CryptInterface::class => create(Crypt::class), + CryptPKIInterface::class => autowire(CryptPKI::class) ->constructorParameter('publicKeyFile', new FileHandler(CryptPKI::PUBLIC_KEY_FILE)) ->constructorParameter('privateKeyFile', new FileHandler(CryptPKI::PRIVATE_KEY_FILE)), - FileCacheInterface::class => create(FileCache::class), - Application::class => autowire(Application::class), - UUIDCookie::class => factory([UUIDCookie::class, 'factory']) + FileCacheInterface::class => create(FileCache::class), + Application::class => autowire(Application::class), + UUIDCookie::class => factory([UUIDCookie::class, 'factory']) ->parameter( 'request', get(RequestInterface::class) diff --git a/lib/SP/Core/Events/Event.php b/lib/SP/Core/Events/Event.php index 68d7107e..5cc3c0af 100644 --- a/lib/SP/Core/Events/Event.php +++ b/lib/SP/Core/Events/Event.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -34,21 +34,20 @@ use SP\Core\Exceptions\SPException; */ class Event { - private object $source; + private object $source; private ?EventMessage $eventMessage; /** * Event constructor. * - * @param object $source + * @param object $source * @param EventMessage|null $eventMessage * */ public function __construct( - object $source, - EventMessage $eventMessage = null - ) - { + object $source, + ?EventMessage $eventMessage = null + ) { $this->source = $source; $this->eventMessage = $eventMessage; } @@ -76,5 +75,4 @@ class Event { return $this->eventMessage; } - -} \ No newline at end of file +} diff --git a/lib/SP/Core/Exceptions/SPException.php b/lib/SP/Core/Exceptions/SPException.php index 38209c68..d8dafd75 100644 --- a/lib/SP/Core/Exceptions/SPException.php +++ b/lib/SP/Core/Exceptions/SPException.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -33,10 +33,6 @@ defined('APP_ROOT') || die(); */ class SPException extends Exception { - /** - * Constantes para tipos de excepción - */ - public const OK = 0; public const CRITICAL = 1; public const WARNING = 2; public const ERROR = 3; @@ -48,17 +44,17 @@ class SPException extends Exception /** * SPException constructor. * - * @param string $message - * @param int $type - * @param string|null $hint - * @param int $code - * @param Exception|null $previous + * @param string $message + * @param int $type + * @param string|null $hint + * @param int $code + * @param Exception|null $previous */ public function __construct( - string $message, - int $type = self::ERROR, - ?string $hint = null, - int $code = 0, + string $message, + int $type = self::ERROR, + ?string $hint = null, + int $code = 0, Exception $previous = null ) { $this->type = $type; @@ -67,12 +63,48 @@ class SPException extends Exception parent::__construct($message, $code, $previous); } + public static function error( + string $message, + ?string $hint = null, + int $code = 0, + Exception $previous = null + ): static { + return new static($message, SPException::ERROR, $hint, $code, $previous); + } + + public static function critical( + string $message, + ?string $hint = null, + int $code = 0, + Exception $previous = null + ): static { + return new static($message, SPException::CRITICAL, $hint, $code, $previous); + } + + public static function warning( + string $message, + ?string $hint = null, + int $code = 0, + Exception $previous = null + ): static { + return new static($message, SPException::WARNING, $hint, $code, $previous); + } + + public static function info( + string $message, + ?string $hint = null, + int $code = 0, + Exception $previous = null + ): static { + return new static($message, SPException::INFO, $hint, $code, $previous); + } + /** * @return string */ - public function __toString() + public function __toString(): string { - return __CLASS__.": [{$this->code}]: {$this->message} ({$this->hint})\n"; + return sprintf('%s: [%s]: %s (%s)', __CLASS__, $this->code, $this->message, $this->hint); } public function getHint(): ?string diff --git a/lib/SP/Domain/Auth/Ports/LdapActionsInterface.php b/lib/SP/Domain/Auth/Ports/LdapActionsInterface.php new file mode 100644 index 00000000..0dbb5af9 --- /dev/null +++ b/lib/SP/Domain/Auth/Ports/LdapActionsInterface.php @@ -0,0 +1,74 @@ +. + */ + +namespace SP\Domain\Auth\Ports; + + +use SP\Providers\Auth\Ldap\AttributeCollection; +use SP\Providers\Auth\Ldap\LdapException; +use SP\Providers\Auth\Ldap\LdapParams; + +/** + * Class LdapActions + * + * @package SP\Providers\Auth\Ldap + */ +interface LdapActionsInterface +{ + /** + * Obtener el RDN del grupo. + * + * @param string $groupFilter + * + * @return array Groups' DN + * @throws LdapException + */ + public function searchGroupsDn(string $groupFilter): array; + + /** + * @param string $filter + * + * @return AttributeCollection + * @throws LdapException + */ + public function getAttributes(string $filter): AttributeCollection; + + /** + * Obtener los objetos según el filtro indicado + * + * @param string $filter + * @param array $attributes + * @param string|null $searchBase + * + * @return array + * @throws LdapException + */ + public function getObjects( + string $filter, + array $attributes, + ?string $searchBase = null + ): array; + + public function mutate(LdapParams $ldapParams): LdapActionsInterface; +} diff --git a/lib/SP/Providers/Auth/Ldap/LdapAuthInterface.php b/lib/SP/Domain/Auth/Ports/LdapAuthInterface.php similarity index 65% rename from lib/SP/Providers/Auth/Ldap/LdapAuthInterface.php rename to lib/SP/Domain/Auth/Ports/LdapAuthInterface.php index 8de21ac6..079feb10 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapAuthInterface.php +++ b/lib/SP/Domain/Auth/Ports/LdapAuthInterface.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -22,10 +22,10 @@ * along with sysPass. If not, see . */ -namespace SP\Providers\Auth\Ldap; - +namespace SP\Domain\Auth\Ports; use SP\Providers\Auth\AuthInterface; +use SP\Providers\Auth\Ldap\LdapAuthData; /** * Class LdapBase @@ -41,24 +41,4 @@ interface LdapAuthInterface extends AuthInterface * @return LdapAuthData */ public function getLdapAuthData(): LdapAuthData; - - /** - * @return string - */ - public function getUserLogin(): ?string; - - /** - * @param string $userLogin - */ - public function setUserLogin(string $userLogin): void; - - /** - * Obtener los atributos del usuario. - * - * @param string $userLogin - * - * @return LdapAuthData con los atributos disponibles y sus valores - * @throws LdapException - */ - public function getAttributes(string $userLogin): LdapAuthData; -} \ No newline at end of file +} diff --git a/lib/SP/Domain/Auth/Ports/LdapCheckServiceInterface.php b/lib/SP/Domain/Auth/Ports/LdapCheckServiceInterface.php index 9a2aec5c..09a4b5c2 100644 --- a/lib/SP/Domain/Auth/Ports/LdapCheckServiceInterface.php +++ b/lib/SP/Domain/Auth/Ports/LdapCheckServiceInterface.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -26,7 +26,6 @@ namespace SP\Domain\Auth\Ports; use SP\Providers\Auth\Ldap\LdapException; -use SP\Providers\Auth\Ldap\LdapParams; /** * Class LdapCheckService @@ -36,29 +35,12 @@ use SP\Providers\Auth\Ldap\LdapParams; interface LdapCheckServiceInterface { /** - * @param LdapParams $ldapParams - * * @throws LdapException */ - public function checkConnection(LdapParams $ldapParams): void; - - /** - * @throws \SP\Providers\Auth\Ldap\LdapException - */ public function getObjects(bool $includeGroups = true): array; /** - * Obtener los datos de una búsqueda de LDAP de un atributo - * - * @param array $data - * @param string[] $attributes - * - * @return array - */ - public function ldapResultsMapper(array $data, array $attributes = ['dn']): array; - - /** - * @throws \SP\Providers\Auth\Ldap\LdapException + * @throws LdapException */ public function getObjectsByFilter(string $filter): array; } diff --git a/lib/SP/Domain/Auth/Ports/LdapConnectionInterface.php b/lib/SP/Domain/Auth/Ports/LdapConnectionInterface.php new file mode 100644 index 00000000..5ed4acc9 --- /dev/null +++ b/lib/SP/Domain/Auth/Ports/LdapConnectionInterface.php @@ -0,0 +1,51 @@ +. + */ + +namespace SP\Domain\Auth\Ports; + +use SP\Providers\Auth\Ldap\LdapException; +use SP\Providers\Auth\Ldap\LdapParams; + +/** + * Interface LdapInterface + * + * @package Auth\Ldap + */ +interface LdapConnectionInterface +{ + /** + * @throws LdapException + */ + public function checkConnection(): void; + + /** + * @throws LdapException + */ + public function connect(?string $bindDn = null, ?string $bindPass = null): void; + + /** + * @throws LdapException + */ + public function mutate(LdapParams $ldapParams): LdapConnectionInterface; +} diff --git a/lib/SP/Providers/Auth/Ldap/LdapInterface.php b/lib/SP/Domain/Auth/Ports/LdapInterface.php similarity index 82% rename from lib/SP/Providers/Auth/Ldap/LdapInterface.php rename to lib/SP/Domain/Auth/Ports/LdapInterface.php index 5259cb6e..d09f131f 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapInterface.php +++ b/lib/SP/Domain/Auth/Ports/LdapInterface.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -22,9 +22,11 @@ * along with sysPass. If not, see . */ -namespace SP\Providers\Auth\Ldap; +namespace SP\Domain\Auth\Ports; +use SP\Providers\Auth\Ldap\LdapException; + /** * Interface LdapInterface * @@ -32,8 +34,6 @@ namespace SP\Providers\Auth\Ldap; */ interface LdapInterface { - public const PAGE_SIZE = 500; - /** * Obtener el filtro para buscar el usuario * @@ -64,7 +64,7 @@ interface LdapInterface * * @param string $userDn * @param string $userLogin - * @param array $groupsDn + * @param array $groupsDn * * @return bool */ @@ -77,28 +77,21 @@ interface LdapInterface */ public function getGroupObjectFilter(): string; - /** - * Connects and binds to an LDAP server - * - * @throws LdapException - */ - public function connect(); - /** * @param string|null $bindDn * @param string|null $bindPass * - * @return bool - */ - public function bind(?string $bindDn = null, ?string $bindPass = null): bool; + * @throws LdapException + **/ + public function connect(?string $bindDn = null, ?string $bindPass = null): void; /** - * @return LdapActions + * @return LdapActionsInterface */ - public function getLdapActions(): LdapActions; + public function getLdapActions(): LdapActionsInterface; /** * @return string */ public function getServer(): string; -} \ No newline at end of file +} diff --git a/lib/SP/Domain/Auth/Services/LdapCheckService.php b/lib/SP/Domain/Auth/Services/LdapCheckService.php index 71e2089c..670387bc 100644 --- a/lib/SP/Domain/Auth/Services/LdapCheckService.php +++ b/lib/SP/Domain/Auth/Services/LdapCheckService.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -24,65 +24,76 @@ namespace SP\Domain\Auth\Services; +use SP\Core\Application; +use SP\Domain\Auth\Ports\LdapActionsInterface; use SP\Domain\Auth\Ports\LdapCheckServiceInterface; +use SP\Domain\Auth\Ports\LdapConnectionInterface; +use SP\Domain\Auth\Ports\LdapInterface; use SP\Domain\Common\Services\Service; -use SP\Providers\Auth\Ldap\Ldap; +use SP\Providers\Auth\Ldap\LdapBase; use SP\Providers\Auth\Ldap\LdapException; -use SP\Providers\Auth\Ldap\LdapInterface; use SP\Providers\Auth\Ldap\LdapParams; /** * Class LdapCheckService - * - * @package SP\Domain\Import\Services */ final class LdapCheckService extends Service implements LdapCheckServiceInterface { - protected ?LdapInterface $ldap = null; - - /** - * @param LdapParams $ldapParams - * - * @throws LdapException - */ - public function checkConnection(LdapParams $ldapParams): void - { - $this->ldap = Ldap::factory( - $ldapParams, - $this->eventDispatcher, - true - ); + public function __construct( + Application $application, + private readonly LdapConnectionInterface $ldapConnection, + private readonly LdapActionsInterface $ldapActions + ) { + parent::__construct($application); } /** - * @throws \SP\Providers\Auth\Ldap\LdapException + * @throws LdapException */ - public function getObjectsByFilter(string $filter): array + public function getObjectsByFilter(string $filter, ?LdapParams $ldapParams = null): array { + $ldap = $this->getLdap($ldapParams); + $objects = $this->ldapResultsMapper( - $this->ldap->getLdapActions()->getObjects($filter, ['dn']) + $ldap->getLdapActions()->getObjects($filter, ['dn']) ); return [ - 'count' => count($objects), + 'count' => count($objects), 'results' => [ [ - 'icon' => '', + 'icon' => '', 'items' => $objects, ], ], ]; } + /** + * @param LdapParams $ldapParams + * + * @return LdapInterface + * @throws LdapException + */ + private function getLdap(LdapParams $ldapParams): LdapInterface + { + return LdapBase::factory( + $this->eventDispatcher, + $this->ldapConnection, + $this->ldapActions, + $ldapParams + ); + } + /** * Obtener los datos de una búsqueda de LDAP de un atributo * - * @param array $data - * @param string[] $attributes + * @param array $data + * @param string[] $attributes * * @return array */ - public function ldapResultsMapper( + private function ldapResultsMapper( array $data, array $attributes = ['dn'] ): array { @@ -108,21 +119,23 @@ final class LdapCheckService extends Service implements LdapCheckServiceInterfac } /** - * @throws \SP\Providers\Auth\Ldap\LdapException + * @throws LdapException */ - public function getObjects(bool $includeGroups = true): array + public function getObjects(bool $includeGroups = true, ?LdapParams $ldapParams = null): array { - $ldapActions = $this->ldap->getLdapActions(); + $ldap = $this->getLdap($ldapParams); + + $ldapActions = $ldap->getLdapActions(); $data = ['count' => 0, 'results' => []]; $indirectFilterItems = $this->ldapResultsMapper( - $ldapActions->getObjects($this->ldap->getGroupMembershipIndirectFilter(), ['dn']) + $ldapActions->getObjects($ldap->getGroupMembershipIndirectFilter(), ['dn']) ); $directFilterItems = $this->ldapResultsMapper( $ldapActions->getObjects( - $this->ldap->getGroupMembershipDirectFilter(), + $ldap->getGroupMembershipDirectFilter(), ['member', 'memberUid', 'uniqueMember'] ), ['member', 'memberUid', 'uniqueMember'] @@ -131,17 +144,17 @@ final class LdapCheckService extends Service implements LdapCheckServiceInterfac $userItems = array_unique(array_merge($indirectFilterItems, $directFilterItems)); $data['results'][] = [ - 'icon' => 'person', + 'icon' => 'person', 'items' => array_values($userItems), ]; if ($includeGroups) { $groupItems = $this->ldapResultsMapper( - $ldapActions->getObjects($this->ldap->getGroupObjectFilter(), ['dn']) + $ldapActions->getObjects($ldap->getGroupObjectFilter(), ['dn']) ); $data['results'][] = [ - 'icon' => 'group', + 'icon' => 'group', 'items' => $groupItems, ]; } diff --git a/lib/SP/Domain/Auth/Services/LoginService.php b/lib/SP/Domain/Auth/Services/LoginService.php index 07478c07..399b3629 100644 --- a/lib/SP/Domain/Auth/Services/LoginService.php +++ b/lib/SP/Domain/Auth/Services/LoginService.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -39,6 +39,7 @@ use SP\Core\LanguageInterface; use SP\Core\UI\ThemeInterface; use SP\DataModel\UserLoginData; use SP\DataModel\UserPreferencesData; +use SP\Domain\Auth\Ports\LdapAuthInterface; use SP\Domain\Auth\Ports\LoginServiceInterface; use SP\Domain\Common\Services\Service; use SP\Domain\Config\Ports\ConfigDataInterface; @@ -58,10 +59,11 @@ use SP\Providers\Auth\AuthProviderInterface; use SP\Providers\Auth\Browser\BrowserAuthData; use SP\Providers\Auth\Database\DatabaseAuthData; use SP\Providers\Auth\Ldap\LdapAuthData; -use SP\Providers\Auth\Ldap\LdapAuthInterface; -use SP\Providers\Auth\Ldap\LdapCode; +use SP\Providers\Auth\Ldap\LdapCodeEnum; use SP\Util\PasswordUtil; +use function SP\__u; + /** * Class LoginService * @@ -98,17 +100,17 @@ final class LoginService extends Service implements LoginServiceInterface * @throws \SP\Domain\Auth\Services\AuthException */ public function __construct( - Application $application, - AuthProviderInterface $authProvider, - ThemeInterface $theme, - LanguageInterface $language, - TrackServiceInterface $trackService, - RequestInterface $request, - UserServiceInterface $userService, - UserPassRecoverServiceInterface $userPassRecoverService, + Application $application, + AuthProviderInterface $authProvider, + ThemeInterface $theme, + LanguageInterface $language, + TrackServiceInterface $trackService, + RequestInterface $request, + UserServiceInterface $userService, + UserPassRecoverServiceInterface $userPassRecoverService, TemporaryMasterPassServiceInterface $temporaryMasterPassService, - UserPassServiceInterface $userPassService, - UserProfileServiceInterface $userProfileService + UserPassServiceInterface $userPassService, + UserProfileServiceInterface $userProfileService ) { parent::__construct($application); @@ -257,8 +259,8 @@ final class LoginService extends Service implements LoginServiceInterface new Event( $this, EventMessage::factory() - ->addDescription(__u('User disabled')) - ->addDetail(__u('User'), $userLoginResponse->getLogin()) + ->addDescription(__u('User disabled')) + ->addDetail(__u('User'), $userLoginResponse->getLogin()) ) ); @@ -284,7 +286,7 @@ final class LoginService extends Service implements LoginServiceInterface $this->userPassRecoverService->add($userLoginResponse->getId(), $hash); $uri = new Uri('index.php'); - $uri->addParam('r', 'userPassReset/reset/'.$hash); + $uri->addParam('r', 'userPassReset/reset/' . $hash); return new LoginResponse(self::STATUS_PASS_RESET, $uri->getUri()); } @@ -457,7 +459,7 @@ final class LoginService extends Service implements LoginServiceInterface } /** - * @param string|null $from + * @param string|null $from */ public function setFrom(?string $from): void { @@ -467,7 +469,7 @@ final class LoginService extends Service implements LoginServiceInterface /** * Autentificación LDAP * - * @param LdapAuthData $authData + * @param LdapAuthData $authData * * @return bool * @throws SPException @@ -475,13 +477,13 @@ final class LoginService extends Service implements LoginServiceInterface */ private function authLdap(LdapAuthData $authData): bool { - if ($authData->getStatusCode() > LdapCode::SUCCESS) { + if ($authData->getStatusCode() > LdapCodeEnum::SUCCESS->value) { $eventMessage = EventMessage::factory() - ->addDetail(__u('Type'), __FUNCTION__) - ->addDetail(__u('LDAP Server'), $authData->getServer()) - ->addDetail(__u('User'), $this->userLoginData->getLoginUser()); + ->addDetail(__u('Type'), __FUNCTION__) + ->addDetail(__u('LDAP Server'), $authData->getServer()) + ->addDetail(__u('User'), $this->userLoginData->getLoginUser()); - if ($authData->getStatusCode() === LdapCode::INVALID_CREDENTIALS) { + if ($authData->getStatusCode() === LdapCodeEnum::INVALID_CREDENTIALS->value) { $eventMessage->addDescription(__u('Wrong login')); $this->addTracking(); @@ -522,7 +524,7 @@ final class LoginService extends Service implements LoginServiceInterface ); } - if ($authData->getStatusCode() === LdapCode::NO_SUCH_OBJECT + if ($authData->getStatusCode() === LdapCodeEnum::NO_SUCH_OBJECT->value || $authData->isAuthoritative() === false ) { $eventMessage->addDescription(__u('Non authoritative auth')); @@ -552,8 +554,8 @@ final class LoginService extends Service implements LoginServiceInterface new Event( $this, EventMessage::factory() - ->addDetail(__u('Type'), __FUNCTION__) - ->addDetail(__u('LDAP Server'), $authData->getServer()) + ->addDetail(__u('Type'), __FUNCTION__) + ->addDetail(__u('LDAP Server'), $authData->getServer()) ) ); @@ -596,7 +598,7 @@ final class LoginService extends Service implements LoginServiceInterface /** * Autentificación en BD * - * @param DatabaseAuthData $authData + * @param DatabaseAuthData $authData * * @return bool * @throws SPException @@ -605,8 +607,8 @@ final class LoginService extends Service implements LoginServiceInterface private function authDatabase(DatabaseAuthData $authData): bool { $eventMessage = EventMessage::factory() - ->addDetail(__u('Type'), __FUNCTION__) - ->addDetail(__u('User'), $this->userLoginData->getLoginUser()); + ->addDetail(__u('Type'), __FUNCTION__) + ->addDetail(__u('User'), $this->userLoginData->getLoginUser()); // Autentificamos con la BBDD if ($authData->getAuthenticated() === false) { @@ -643,7 +645,7 @@ final class LoginService extends Service implements LoginServiceInterface /** * Comprobar si el cliente ha enviado las variables de autentificación * - * @param BrowserAuthData $authData + * @param BrowserAuthData $authData * * @return bool * @throws AuthException @@ -653,9 +655,12 @@ final class LoginService extends Service implements LoginServiceInterface $authType = $this->request->getServer('AUTH_TYPE') ?: __('N/A'); $eventMessage = EventMessage::factory() - ->addDetail(__u('Type'), __FUNCTION__) - ->addDetail(__u('User'), $this->userLoginData->getLoginUser()) - ->addDetail(__u('Authentication'), sprintf('%s (%s)', $authType, $authData->getName())); + ->addDetail(__u('Type'), __FUNCTION__) + ->addDetail(__u('User'), $this->userLoginData->getLoginUser()) + ->addDetail( + __u('Authentication'), + sprintf('%s (%s)', $authType, $authData->getName()) + ); // Comprobar si concide el login con la autentificación del servidor web if ($authData->getAuthenticated() === false) { diff --git a/lib/SP/Domain/Config/Services/UpgradeConfigService.php b/lib/SP/Domain/Config/Services/UpgradeConfigService.php index e779fb47..0f36b705 100644 --- a/lib/SP/Domain/Config/Services/UpgradeConfigService.php +++ b/lib/SP/Domain/Config/Services/UpgradeConfigService.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -34,10 +34,12 @@ use SP\Domain\Config\Ports\ConfigDataInterface; use SP\Domain\Config\Ports\UpgradeConfigServiceInterface; use SP\Domain\Upgrade\Services\UpgradeException; use SP\Infrastructure\File\FileException; -use SP\Providers\Auth\Ldap\LdapTypeInterface; +use SP\Providers\Auth\Ldap\LdapTypeEnum; use SP\Providers\Log\FileLogHandler; use SP\Util\VersionUtil; +use function SP\__u; + /** * Class UpgradeService * @@ -75,7 +77,7 @@ final class UpgradeConfigService extends Service implements UpgradeConfigService /** * Actualizar el archivo de configuración a formato XML * - * @throws \SP\Domain\Upgrade\Services\UpgradeException + * @throws UpgradeException */ public function upgradeOldConfigFile(string $version): void { @@ -111,7 +113,7 @@ final class UpgradeConfigService extends Service implements UpgradeConfigService } } - $oldFile = OLD_CONFIG_FILE.'.old.'.time(); + $oldFile = OLD_CONFIG_FILE . '.old.' . time(); try { $configData->setSiteTheme('material-blue'); @@ -132,8 +134,8 @@ final class UpgradeConfigService extends Service implements UpgradeConfigService new Event( $this, EventMessage::factory() - ->addDescription(__u('Error while updating the configuration')) - ->addDetail(__u('File'), $oldFile) + ->addDescription(__u('Error while updating the configuration')) + ->addDetail(__u('File'), $oldFile) ) ); @@ -147,58 +149,58 @@ final class UpgradeConfigService extends Service implements UpgradeConfigService private static function getConfigParams(): array { return [ - 'setAccountCount' => 'account_count', - 'setAccountLink' => 'account_link', - 'setCheckUpdates' => 'checkupdates', - 'setCheckNotices' => 'checknotices', - 'setDbHost' => 'dbhost', - 'setDbName' => 'dbname', - 'setDbPass' => 'dbpass', - 'setDbUser' => 'dbuser', - 'setDebug' => 'debug', - 'setDemoEnabled' => 'demo_enabled', - 'setGlobalSearch' => 'globalsearch', - 'setInstalled' => 'installed', - 'setMaintenance' => 'maintenance', - 'setPasswordSalt' => 'passwordsalt', - 'setSessionTimeout' => 'session_timeout', - 'setSiteLang' => 'sitelang', - 'setConfigVersion' => 'version', - 'setConfigHash' => 'config_hash', - 'setProxyEnabled' => 'proxy_enabled', - 'setProxyPass' => 'proxy_pass', - 'setProxyPort' => 'proxy_port', - 'setProxyServer' => 'proxy_server', - 'setProxyUser' => 'proxy_user', - 'setResultsAsCards' => 'resultsascards', - 'setSiteTheme' => 'sitetheme', - 'setAccountPassToImage' => 'account_passtoimage', - 'setFilesAllowedExts' => ['allowed_exts', 'files_allowed_exts'], - 'setFilesAllowedSize' => ['allowed_size', 'files_allowed_size'], - 'setFilesEnabled' => ['filesenabled', 'files_enabled'], - 'setLdapBase' => ['ldapbase', 'ldap_base'], - 'setLdapBindPass' => ['ldapbindpass', 'ldap_bindpass'], - 'setLdapBindUser' => ['ldapbinduser', 'ldap_binduser'], - 'setLdapEnabled' => ['ldapenabled', 'ldap_enabled'], - 'setLdapGroup' => ['ldapgroup', 'ldap_group'], - 'setLdapServer' => ['ldapserver', 'ldap_server'], - 'setLdapAds' => 'ldap_ads', - 'setLdapDefaultGroup' => 'ldap_defaultgroup', - 'setLdapDefaultProfile' => 'ldap_defaultprofile', - 'setLogEnabled' => ['logenabled', 'log_enabled'], - 'setMailEnabled' => ['mailenabled', 'mail_enabled'], - 'setMailFrom' => ['mailfrom', 'mail_from'], - 'setMailPass' => ['mailpass', 'mail_pass'], - 'setMailPort' => ['mailport', 'mail_port'], + 'setAccountCount' => 'account_count', + 'setAccountLink' => 'account_link', + 'setCheckUpdates' => 'checkupdates', + 'setCheckNotices' => 'checknotices', + 'setDbHost' => 'dbhost', + 'setDbName' => 'dbname', + 'setDbPass' => 'dbpass', + 'setDbUser' => 'dbuser', + 'setDebug' => 'debug', + 'setDemoEnabled' => 'demo_enabled', + 'setGlobalSearch' => 'globalsearch', + 'setInstalled' => 'installed', + 'setMaintenance' => 'maintenance', + 'setPasswordSalt' => 'passwordsalt', + 'setSessionTimeout' => 'session_timeout', + 'setSiteLang' => 'sitelang', + 'setConfigVersion' => 'version', + 'setConfigHash' => 'config_hash', + 'setProxyEnabled' => 'proxy_enabled', + 'setProxyPass' => 'proxy_pass', + 'setProxyPort' => 'proxy_port', + 'setProxyServer' => 'proxy_server', + 'setProxyUser' => 'proxy_user', + 'setResultsAsCards' => 'resultsascards', + 'setSiteTheme' => 'sitetheme', + 'setAccountPassToImage' => 'account_passtoimage', + 'setFilesAllowedExts' => ['allowed_exts', 'files_allowed_exts'], + 'setFilesAllowedSize' => ['allowed_size', 'files_allowed_size'], + 'setFilesEnabled' => ['filesenabled', 'files_enabled'], + 'setLdapBase' => ['ldapbase', 'ldap_base'], + 'setLdapBindPass' => ['ldapbindpass', 'ldap_bindpass'], + 'setLdapBindUser' => ['ldapbinduser', 'ldap_binduser'], + 'setLdapEnabled' => ['ldapenabled', 'ldap_enabled'], + 'setLdapGroup' => ['ldapgroup', 'ldap_group'], + 'setLdapServer' => ['ldapserver', 'ldap_server'], + 'setLdapAds' => 'ldap_ads', + 'setLdapDefaultGroup' => 'ldap_defaultgroup', + 'setLdapDefaultProfile' => 'ldap_defaultprofile', + 'setLogEnabled' => ['logenabled', 'log_enabled'], + 'setMailEnabled' => ['mailenabled', 'mail_enabled'], + 'setMailFrom' => ['mailfrom', 'mail_from'], + 'setMailPass' => ['mailpass', 'mail_pass'], + 'setMailPort' => ['mailport', 'mail_port'], 'setMailRequestsEnabled' => ['mailrequestsenabled', 'mail_requestsenabled'], - 'setMailAuthenabled' => 'mail_authenabled', - 'setMailSecurity' => ['mailsecurity', 'mail_security'], - 'setMailServer' => ['mailserver', 'mail_server'], - 'setMailUser' => ['mailuser', 'mail_user'], - 'setWikiEnabled' => ['wikienabled', 'wiki_enabled'], - 'setWikiFilter' => ['wikifilter', 'wiki_filter'], - 'setWikiPageUrl' => ['wikipageurl'.'wiki_pageurl'], - 'setWikiSearchUrl' => ['wikisearchurl', 'wiki_searchurl'], + 'setMailAuthenabled' => 'mail_authenabled', + 'setMailSecurity' => ['mailsecurity', 'mail_security'], + 'setMailServer' => ['mailserver', 'mail_server'], + 'setMailUser' => ['mailuser', 'mail_user'], + 'setWikiEnabled' => ['wikienabled', 'wiki_enabled'], + 'setWikiFilter' => ['wikifilter', 'wiki_filter'], + 'setWikiPageUrl' => ['wikipageurl' . 'wiki_pageurl'], + 'setWikiSearchUrl' => ['wikisearchurl', 'wiki_searchurl'], ]; } @@ -240,7 +242,7 @@ final class UpgradeConfigService extends Service implements UpgradeConfigService } /** - * @throws \SP\Infrastructure\File\FileException + * @throws FileException */ private function upgrade_200_17011202(string $version): void { @@ -254,8 +256,8 @@ final class UpgradeConfigService extends Service implements UpgradeConfigService new Event( $this, EventMessage::factory() - ->addDescription(__u('Update Configuration')) - ->addDetail(__u('Version'), $version) + ->addDescription(__u('Update Configuration')) + ->addDetail(__u('Version'), $version) ) ); } @@ -281,9 +283,9 @@ final class UpgradeConfigService extends Service implements UpgradeConfigService new Event( $this, EventMessage::factory() - ->addDescription(__u('MIME type set for this extension')) - ->addDetail(__u('MIME type'), $mimeType['type']) - ->addDetail(__u('Extension'), $extension) + ->addDescription(__u('MIME type set for this extension')) + ->addDetail(__u('MIME type'), $mimeType['type']) + ->addDetail(__u('Extension'), $extension) ) ); } @@ -295,8 +297,8 @@ final class UpgradeConfigService extends Service implements UpgradeConfigService new Event( $this, EventMessage::factory() - ->addDescription(__u('MIME type not found for this extension')) - ->addDetail(__u('Extension'), $extension) + ->addDescription(__u('MIME type not found for this extension')) + ->addDetail(__u('Extension'), $extension) ) ); } @@ -312,22 +314,22 @@ final class UpgradeConfigService extends Service implements UpgradeConfigService new Event( $this, EventMessage::factory() - ->addDescription(__u('Update Configuration')) - ->addDetail(__u('Version'), $version) + ->addDescription(__u('Update Configuration')) + ->addDetail(__u('Version'), $version) ) ); } /** - * @throws \SP\Infrastructure\File\FileException + * @throws FileException */ private function upgrade_300_18112501(string $version): void { if ($this->configData->isLdapEnabled()) { if ($this->configData->get('ldapAds')) { - $this->configData->setLdapType(LdapTypeInterface::LDAP_ADS); + $this->configData->setLdapType(LdapTypeEnum::ADS->value); } else { - $this->configData->setLdapType(LdapTypeInterface::LDAP_STD); + $this->configData->setLdapType(LdapTypeEnum::STD->value); } $this->configData->setConfigVersion($version); @@ -339,21 +341,21 @@ final class UpgradeConfigService extends Service implements UpgradeConfigService new Event( $this, EventMessage::factory() - ->addDescription(__u('Update Configuration')) - ->addDetail(__u('Version'), $version) + ->addDescription(__u('Update Configuration')) + ->addDetail(__u('Version'), $version) ) ); } } /** - * @throws \SP\Infrastructure\File\FileException + * @throws FileException */ private function upgrade_320_20062801(string $version): void { if ($this->configData->isLdapEnabled()) { - if ($this->configData->get('ldapType') === LdapTypeInterface::LDAP_AZURE) { - $this->configData->setLdapType(LdapTypeInterface::LDAP_ADS); + if ($this->configData->get('ldapType') === LdapTypeEnum::AZURE->value) { + $this->configData->setLdapType(LdapTypeEnum::ADS->value); } $this->configData->setConfigVersion($version); @@ -365,8 +367,8 @@ final class UpgradeConfigService extends Service implements UpgradeConfigService new Event( $this, EventMessage::factory() - ->addDescription(__u('Update Configuration')) - ->addDetail(__u('Version'), $version) + ->addDescription(__u('Update Configuration')) + ->addDetail(__u('Version'), $version) ) ); } diff --git a/lib/SP/Domain/Import/Services/LdapImportService.php b/lib/SP/Domain/Import/Services/LdapImportService.php index e5dabe90..0d631c64 100644 --- a/lib/SP/Domain/Import/Services/LdapImportService.php +++ b/lib/SP/Domain/Import/Services/LdapImportService.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -30,13 +30,13 @@ use SP\Core\Events\Event; use SP\Core\Events\EventMessage; use SP\DataModel\UserData; use SP\DataModel\UserGroupData; +use SP\Domain\Auth\Ports\LdapInterface; use SP\Domain\Common\Services\Service; use SP\Domain\Import\Ports\LdapImportServiceInterface; use SP\Domain\User\Ports\UserServiceInterface; use SP\Domain\User\Services\UserGroupService; -use SP\Providers\Auth\Ldap\Ldap; +use SP\Providers\Auth\Ldap\LdapBase; use SP\Providers\Auth\Ldap\LdapException; -use SP\Providers\Auth\Ldap\LdapInterface; use SP\Providers\Auth\Ldap\LdapParams; /** @@ -155,7 +155,7 @@ final class LdapImportService extends Service implements LdapImportServiceInterf */ protected function getLdap(LdapParams $ldapParams): LdapInterface { - return Ldap::factory( + return LdapBase::factory( $ldapParams, $this->eventDispatcher, $this->config->getConfigData()->isDebug() diff --git a/lib/SP/Providers/Auth/AuthDataBase.php b/lib/SP/Providers/Auth/AuthDataBase.php index c68eecad..3d9a25bb 100644 --- a/lib/SP/Providers/Auth/AuthDataBase.php +++ b/lib/SP/Providers/Auth/AuthDataBase.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -25,127 +25,78 @@ namespace SP\Providers\Auth; /** - * Class AuthData - * - * @package Auth + * Class AuthDataBase */ abstract class AuthDataBase { - /** - * @var string - */ - protected string $name; - /** - * @var string - */ - protected string $email; - /** - * @var bool - */ - protected ?bool $authenticated; - /** - * @var int - */ - protected int $statusCode = 0; - /** - * @var string - */ - protected string $server; - /** - * @var bool - */ - protected bool $authoritative = false; - /** - * @var bool - */ - protected bool $failed = false; + protected ?string $name = null; + protected ?string $email = null; + protected ?bool $authenticated = null; + protected int $statusCode = 0; + protected ?string $server = null; + protected bool $failed = false; /** - * @return string|null + * @param bool $authoritative Whether this authentication is required to access to the application */ + public function __construct(private readonly bool $authoritative = false) + { + } + public function getName(): ?string { return $this->name; } - /** - * @param string $name - */ - public function setName(string $name) + public function setName(string $name): void { $this->name = $name; } - /** - * @return string|null - */ public function getEmail(): ?string { return $this->email; } - /** - * @param string $email - */ public function setEmail(string $email): void { $this->email = $email; } - /** - * @return int|null - */ public function getAuthenticated(): ?int { return $this->authenticated; } - /** - * @param bool $authenticated - * - * @return $this - */ - public function setAuthenticated(?bool $authenticated = null): AuthDataBase + public function setAuthenticated(?bool $authenticated = null): static { $this->authenticated = $authenticated; return $this; } - /** - * @return string|null - */ public function getServer(): ?string { return $this->server; } - /** - * @param string $server - */ - public function setServer(string $server) + public function setServer(string $server): void { $this->server = $server; } - /** - * @return int - */ public function getStatusCode(): int { - return (int)$this->statusCode; + return $this->statusCode; } - /** - * @param int $statusCode - */ public function setStatusCode(int $statusCode): void { $this->statusCode = $statusCode; } /** - * Indica si es requerida para acceder a la aplicación + * Whether this authentication is required to access to the application * * @return bool */ @@ -154,29 +105,13 @@ abstract class AuthDataBase return $this->authoritative; } - /** - * Indica si es requerida para acceder a la aplicación - * - * @param bool $authoritative - */ - public function setAuthoritative(bool $authoritative): void - { - $this->authoritative = $authoritative; - } - - /** - * @return bool|null - */ - public function isFailed(): ?bool + public function isFailed(): bool { return $this->failed; } - /** - * @param bool $failed - */ - public function setFailed(bool $failed) + public function setFailed(bool $failed): void { $this->failed = $failed; } -} \ No newline at end of file +} diff --git a/lib/SP/Providers/Auth/AuthProvider.php b/lib/SP/Providers/Auth/AuthProvider.php index 26c4fb5e..ac25ffc6 100644 --- a/lib/SP/Providers/Auth/AuthProvider.php +++ b/lib/SP/Providers/Auth/AuthProvider.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -27,10 +27,10 @@ namespace SP\Providers\Auth; use SP\Core\Application; use SP\Core\Exceptions\SPException; use SP\DataModel\UserLoginData; +use SP\Domain\Auth\Ports\LdapAuthInterface; use SP\Domain\Auth\Services\AuthException; use SP\Providers\Auth\Browser\BrowserAuthInterface; use SP\Providers\Auth\Database\DatabaseAuthInterface; -use SP\Providers\Auth\Ldap\LdapAuthInterface; use SP\Providers\Provider; defined('APP_ROOT') || die(); diff --git a/lib/SP/Providers/Auth/AuthProviderInterface.php b/lib/SP/Providers/Auth/AuthProviderInterface.php index fb4390c7..91133786 100644 --- a/lib/SP/Providers/Auth/AuthProviderInterface.php +++ b/lib/SP/Providers/Auth/AuthProviderInterface.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -26,9 +26,9 @@ namespace SP\Providers\Auth; use SP\DataModel\UserLoginData; +use SP\Domain\Auth\Ports\LdapAuthInterface; use SP\Domain\Auth\Services\AuthException; use SP\Providers\Auth\Browser\BrowserAuthInterface; -use SP\Providers\Auth\Ldap\LdapAuthInterface; /** * Class Auth @@ -58,4 +58,4 @@ interface AuthProviderInterface public function withLdapAuth(LdapAuthInterface $ldapAuth): void; public function withBrowserAuth(BrowserAuthInterface $browserAuth): void; -} \ No newline at end of file +} diff --git a/lib/SP/Providers/Auth/Ldap/Ldap.php b/lib/SP/Providers/Auth/Ldap/Ldap.php deleted file mode 100644 index a768d526..00000000 --- a/lib/SP/Providers/Auth/Ldap/Ldap.php +++ /dev/null @@ -1,174 +0,0 @@ -. - */ - -namespace SP\Providers\Auth\Ldap; - -use SP\Core\Events\EventDispatcher; - -/** - * Class Ldap - * - * @package SP\Providers\Auth\Ldap - */ -abstract class Ldap implements LdapInterface -{ - /** - * @var LdapParams - */ - protected LdapParams $ldapParams; - /** - * @var EventDispatcher - */ - protected EventDispatcher $eventDispatcher; - /** - * @var LdapActions - */ - protected LdapActions $ldapActions; - /** - * @var LdapConnectionInterface - */ - protected LdapConnectionInterface $ldapConnection; - /** - * @var string - */ - protected string $server; - - /** - * LdapBase constructor. - * - * @param LdapConnectionInterface $ldapConnection - * @param \SP\Providers\Auth\Ldap\LdapActions $ldapActions - * @param EventDispatcher $eventDispatcher - */ - public function __construct( - LdapConnectionInterface $ldapConnection, - LdapActions $ldapActions, - EventDispatcher $eventDispatcher - ) { - $this->eventDispatcher = $eventDispatcher; - $this->ldapActions = $ldapActions; - $this->ldapConnection = $ldapConnection; - $this->ldapParams = $ldapConnection->getLdapParams(); - $this->server = $this->pickServer(); - $this->ldapConnection->setServer($this->server); - } - - /** - * Obtener el servidor de LDAP a utilizar - * - * @return mixed - */ - abstract protected function pickServer(): string; - - /** - * @param LdapParams $ldapParams - * @param EventDispatcher $eventDispatcher - * @param bool $debug - * - * @return LdapInterface - * @throws LdapException - */ - public static function factory( - LdapParams $ldapParams, - EventDispatcher $eventDispatcher, - bool $debug - ): LdapInterface { - $ldapConnection = new LdapConnection($ldapParams, $eventDispatcher, $debug); - $ldapConnection->checkConnection(); - - $ldapActions = new LdapActions($ldapConnection, $eventDispatcher); - - switch ($ldapParams->getType()) { - case LdapTypeInterface::LDAP_STD: - return new LdapStd($ldapConnection, $ldapActions, $eventDispatcher); - case LdapTypeInterface::LDAP_ADS: - return new LdapMsAds($ldapConnection, $ldapActions, $eventDispatcher); - } - - throw new LdapException(__u('LDAP type not set')); - } - - /** - * @return LdapActions - */ - public function getLdapActions(): LdapActions - { - return $this->ldapActions; - } - - /** - * Realizar la conexión al servidor de LDAP. - * - * @return resource - * @throws LdapException - */ - public function connect() - { - return $this->ldapConnection->connectAndBind(); - } - - /** - * @param string|null $bindDn - * @param string|null $bindPass - * - * @return bool - */ - public function bind(?string $bindDn = null, ?string $bindPass = null): bool - { - return $this->ldapConnection->bind($bindDn, $bindPass); - } - - /** - * @return string - */ - public function getServer(): string - { - return $this->server; - } - - /** - * @return string - */ - protected function getGroupFromParams(): string - { - if (stripos($this->ldapParams->getGroup(), 'cn') === 0) { - return LdapUtil::getGroupName($this->ldapParams->getGroup()) ?: ''; - } - - return $this->ldapParams->getGroup(); - } - - /** - * @return string - * @throws LdapException - */ - protected function getGroupDn(): string - { - if (stripos($this->ldapParams->getGroup(), 'cn') === 0) { - return $this->ldapParams->getGroup(); - } - - return $this->ldapActions->searchGroupsDn($this->getGroupObjectFilter())[0]; - } -} diff --git a/lib/SP/Providers/Auth/Ldap/LdapActions.php b/lib/SP/Providers/Auth/Ldap/LdapActions.php index 45927ec3..501a9a80 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapActions.php +++ b/lib/SP/Providers/Auth/Ldap/LdapActions.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -22,26 +22,24 @@ * along with sysPass. If not, see . */ -/** @noinspection PhpComposerExtensionStubsInspection */ - namespace SP\Providers\Auth\Ldap; +use Laminas\Ldap\Collection; +use Laminas\Ldap\Exception\LdapException as LaminasLdapException; +use Laminas\Ldap\Ldap as LaminasLdap; use SP\Core\Events\Event; -use SP\Core\Events\EventDispatcher; +use SP\Core\Events\EventDispatcherInterface; use SP\Core\Events\EventMessage; -use SP\Core\Exceptions\SPException; - +use SP\Domain\Auth\Ports\LdapActionsInterface; +use function SP\__u; /** * Class LdapActions * * @package SP\Providers\Auth\Ldap */ -final class LdapActions +final class LdapActions implements LdapActionsInterface { - /** - * Atributos de búsqueda - */ public const USER_ATTRIBUTES = [ 'dn', 'displayname', @@ -55,217 +53,176 @@ final class LdapActions 'givenname', 'sn', 'userprincipalname', - 'cn' + 'cn', ]; public const ATTRIBUTES_MAPPING = [ - 'dn' => 'dn', + 'dn' => 'dn', 'groupmembership' => 'group', - 'memberof' => 'group', - 'displayname' => 'fullname', - 'fullname' => 'fullname', - 'givenname' => 'name', - 'sn' => 'sn', - 'mail' => 'mail', - 'lockouttime' => 'expire' + 'memberof' => 'group', + 'displayname' => 'fullname', + 'fullname' => 'fullname', + 'givenname' => 'name', + 'sn' => 'sn', + 'mail' => 'mail', + 'lockouttime' => 'expire', ]; /** - * @var LdapParams - */ - private LdapParams $ldapParams; - /** - * @var resource - */ - private $ldapHandler; - /** - * @var EventDispatcher - */ - private EventDispatcher $eventDispatcher; - - /** - * LdapActions constructor. - * - * @param LdapConnectionInterface $ldapConnection - * @param EventDispatcher $eventDispatcher - * - * @throws LdapException + * @param LaminasLdap $ldap + * @param LdapParams $ldapParams + * @param EventDispatcherInterface $eventDispatcher */ public function __construct( - LdapConnectionInterface $ldapConnection, - EventDispatcher $eventDispatcher - ) - { - $this->ldapHandler = $ldapConnection->connectAndBind(); - $this->ldapParams = $ldapConnection->getLdapParams(); - $this->eventDispatcher = $eventDispatcher; - } + private readonly LaminasLdap $ldap, + private readonly LdapParams $ldapParams, + private readonly EventDispatcherInterface $eventDispatcher + ) {} /** * Obtener el RDN del grupo. * - * @param string $groupFilter + * @param string $groupFilter * * @return array Groups' DN * @throws LdapException */ public function searchGroupsDn(string $groupFilter): array { - $filter = '(&(cn=' - . ldap_escape($this->getGroupFromParams(), null, LDAP_ESCAPE_FILTER) - . ')' - . $groupFilter - . ')'; + $group = $this->getGroupFromParams(); + + /** @noinspection PhpComposerExtensionStubsInspection */ + $filter = sprintf( + '(&(cn=%s)%s)', + ldap_escape( + $group, + null, + LDAP_ESCAPE_FILTER + ), + $groupFilter + ); $searchResults = $this->getResults($filter, ['dn']); - if ((int)$searchResults['count'] === 0) { - $this->eventDispatcher->notifyEvent('ldap.search.group', - new Event($this, EventMessage::factory() - ->addDescription(__u('Error while searching the group RDN')) - ->addDetail(__u('Group'), $this->getGroupFromParams()) - ->addDetail('LDAP ERROR', LdapConnection::getLdapErrorMessage($this->ldapHandler)) - ->addDetail('LDAP FILTER', $filter)) + if ($searchResults->count() === 0) { + $this->eventDispatcher->notifyEvent( + 'ldap.search.group', + new Event( + $this, + EventMessage::factory() + ->addDescription(__u('Error while searching the group RDN')) + ->addDetail(__u('Group'), $group) + ->addDetail('LDAP ERROR', $this->ldap->getLastError()) + ->addDetail('LDAP FILTER', $filter) + ) ); - throw new LdapException( + throw LdapException::error( __u('Error while searching the group RDN'), - SPException::ERROR, null, - LdapCode::NO_SUCH_OBJECT + LdapCodeEnum::NO_SUCH_OBJECT->value ); } - return array_filter(array_map( - static function ($value) { - if (is_array($value)) { - return $value['dn']; - } + return array_values( + array_filter( + array_map( + static function ($value) { + if (is_array($value)) { + return $value['dn']; + } - return null; - }, - $searchResults) + return null; + }, + $searchResults->toArray() + ) + ) ); } - /** - * @return string - */ protected function getGroupFromParams(): string { if (stripos($this->ldapParams->getGroup(), 'cn') === 0) { return LdapUtil::getGroupName($this->ldapParams->getGroup()) ?: ''; } - return $this->ldapParams->getGroup(); + return $this->ldapParams->getGroup() ?: '*'; } /** - * Devolver los resultados de una paginación + * Get LDAP search results as a Collection * - * @param string $filter Filtro a utilizar - * @param array|null $attributes Atributos a devolver - * @param string|null $searchBase + * @param string $filter Filtro a utilizar + * @param array|null $attributes Atributos a devolver + * @param string|null $searchBase * - * @return bool|array + * @return Collection + * @throws LdapException */ protected function getResults( - string $filter, - ?array $attributes = null, + string $filter, + ?array $attributes = [], ?string $searchBase = null - ) - { - $cookie = ''; - $results = []; - + ): Collection { if (empty($searchBase)) { $searchBase = $this->ldapParams->getSearchBase(); } - do { - ldap_control_paged_result( - $this->ldapHandler, - LdapInterface::PAGE_SIZE, - false, - $cookie - ); + try { + return $this->ldap->search($filter, $searchBase, LaminasLdap::SEARCH_SCOPE_SUB, $attributes); + } catch (LaminasLdapException $e) { + $this->eventDispatcher->notifyEvent('exception', new Event($e)); - $searchRes = @ldap_search( - $this->ldapHandler, - $searchBase, - $filter, - $attributes - ); - - if (!$searchRes) { - return false; - } - - $entries = @ldap_get_entries($this->ldapHandler, $searchRes); - - if (!$entries) { - return false; - } - - $results = array_merge($results, $entries); - - ldap_control_paged_result_response( - $this->ldapHandler, - $searchRes, - $cookie - ); - } while (!empty($cookie)); - - return $results; + throw LdapException::error($e->getMessage(), null, $e->getCode(), $e); + } } /** - * Obtener los atributos del usuario. - * - * @param string $filter + * @param string $filter * * @return AttributeCollection * @throws LdapException */ public function getAttributes(string $filter): AttributeCollection { - $searchResults = $this->getObjects($filter); + $searchResults = $this->getResults($filter) + ->getFirst(); - if ((int)$searchResults['count'] === 0) { - $this->eventDispatcher->notifyEvent('ldap.getAttributes', - new Event($this, EventMessage::factory() - ->addDescription(__u('Error while searching the user on LDAP')) - ->addDetail('LDAP FILTER', $filter)) - ); - - throw new LdapException( - __u('Error while searching the user on LDAP'), - SPException::ERROR, - null, - LdapCode::NO_SUCH_OBJECT - ); + if ($searchResults === null) { + return new AttributeCollection(); } // Normalize keys for comparing - $results = array_change_key_case($searchResults[0], CASE_LOWER); + $results = array_change_key_case($searchResults); $attributeCollection = new AttributeCollection(); - foreach (self::ATTRIBUTES_MAPPING as $attribute => $map) { - if (isset($results[$attribute])) { - if (is_array($results[$attribute])) { - if ((int)$results[$attribute]['count'] > 1) { - unset($results[$attribute]['count']); + $attributes = array_filter( + self::ATTRIBUTES_MAPPING, + fn($attribute) => isset($results[$attribute]), + ARRAY_FILTER_USE_KEY + ); - // Store the whole array - $attributeCollection->set($map, $results[$attribute]); - } else { - // Store first value - $attributeCollection->set($map, trim($results[$attribute][0])); - } + foreach ($attributes as $attribute => $map) { + if (is_array($results[$attribute])) { + if ((int)$results[$attribute]['count'] > 1) { + // Store the whole array + $attributeCollection->set( + $map, + array_filter($results[$attribute], fn($key) => $key !== 'count', ARRAY_FILTER_USE_KEY) + ); } else { - $attributeCollection->set($map, trim($results[$attribute])); + // Store first value + $attributeCollection->set( + $map, + trim($results[$attribute][0]) + ); } + } else { + $attributeCollection->set( + $map, + trim($results[$attribute]) + ); } } @@ -273,39 +230,26 @@ final class LdapActions } /** - * Obtener los objetos según el filtro indicado + * Get LDAP search results as an array * - * @param string $filter - * @param array $attributes - * @param string|null $searchBase + * @param string $filter + * @param array $attributes + * @param string|null $searchBase * * @return array * @throws LdapException */ public function getObjects( - string $filter, - array $attributes = self::USER_ATTRIBUTES, + string $filter, + array $attributes = self::USER_ATTRIBUTES, ?string $searchBase = null - ): array + ): array { + return $this->getResults($filter, $attributes, $searchBase) + ->toArray(); + } + + public function mutate(LdapParams $ldapParams): LdapActionsInterface { - $searchResults = $this->getResults($filter, $attributes, $searchBase); - - if ($searchResults === false) { - $this->eventDispatcher->notifyEvent('ldap.search', - new Event($this, EventMessage::factory() - ->addDescription(__u('Error while searching objects in base DN')) - ->addDetail('LDAP ERROR', LdapConnection::getLdapErrorMessage($this->ldapHandler)) - ->addDetail('LDAP FILTER', $filter)) - ); - - throw new LdapException( - __u('Error while searching objects in base DN'), - SPException::ERROR, - null, - LdapCode::OPERATIONS_ERROR - ); - } - - return $searchResults; + return new self($this->ldap, $ldapParams, $this->eventDispatcher); } } diff --git a/lib/SP/Providers/Auth/Ldap/LdapAuth.php b/lib/SP/Providers/Auth/Ldap/LdapAuth.php index 59ecd867..5447fa74 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapAuth.php +++ b/lib/SP/Providers/Auth/Ldap/LdapAuth.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -24,10 +24,18 @@ namespace SP\Providers\Auth\Ldap; +use SP\Core\Events\Event; use SP\Core\Events\EventDispatcher; +use SP\Core\Events\EventDispatcherInterface; +use SP\Core\Events\EventMessage; use SP\DataModel\UserLoginData; +use SP\Domain\Auth\Ports\LdapAuthInterface; +use SP\Domain\Auth\Ports\LdapInterface; use SP\Domain\Config\Ports\ConfigDataInterface; +use function SP\__u; +use function SP\processException; + /** * Class LdapBase * @@ -35,30 +43,31 @@ use SP\Domain\Config\Ports\ConfigDataInterface; */ final class LdapAuth implements LdapAuthInterface { - protected string $userLogin; - protected LdapAuthData $ldapAuthData; - protected EventDispatcher $eventDispatcher; - protected string $server; - private LdapInterface $ldap; - private ConfigDataInterface $configData; + private readonly LdapAuthData $ldapAuthData; /** * LdapBase constructor. * - * @param LdapInterface $ldap - * @param EventDispatcher $eventDispatcher - * @param \SP\Domain\Config\Ports\ConfigDataInterface $configData + * @param LdapInterface $ldap + * @param EventDispatcher $eventDispatcher + * @param ConfigDataInterface $configData */ public function __construct( - LdapInterface $ldap, - EventDispatcher $eventDispatcher, - ConfigDataInterface $configData + private readonly LdapInterface $ldap, + private readonly EventDispatcherInterface $eventDispatcher, + private readonly ConfigDataInterface $configData ) { - $this->ldap = $ldap; - $this->eventDispatcher = $eventDispatcher; - $this->configData = $configData; + $this->ldapAuthData = new LdapAuthData($this->isAuthGranted()); + } - $this->ldapAuthData = new LdapAuthData(); + /** + * Indica si es requerida para acceder a la aplicación + * + * @return bool + */ + public function isAuthGranted(): bool + { + return !$this->configData->isLdapDatabaseEnabled(); } /** @@ -69,46 +78,33 @@ final class LdapAuth implements LdapAuthInterface return $this->ldapAuthData; } - /** - * @return string - */ - public function getUserLogin(): ?string - { - return $this->userLogin; - } - - /** - * @param string $userLogin - */ - public function setUserLogin(string $userLogin): void - { - $this->userLogin = strtolower($userLogin); - } /** * Autentificar al usuario * - * @param UserLoginData $userLoginData Datos del usuario + * @param UserLoginData $userLoginData Datos del usuario * * @return bool */ public function authenticate(UserLoginData $userLoginData): bool { try { - $this->ldapAuthData->setAuthoritative($this->isAuthGranted()); $this->ldapAuthData->setServer($this->ldap->getServer()); - $this->setUserLogin($userLoginData->getLoginUser()); - $this->ldap->connect(); $this->getAttributes($userLoginData->getLoginUser()); - $this->ldap->bind($this->ldapAuthData->getDn(), $userLoginData->getLoginPass()); + $this->ldap->connect($this->ldapAuthData->getDn(), $userLoginData->getLoginPass()); + + $this->ldapAuthData->setFailed(false); + $this->ldapAuthData->setAuthenticated(true); } catch (LdapException $e) { processException($e); $this->ldapAuthData->setStatusCode($e->getCode()); + $this->ldapAuthData->setAuthenticated(false); + $this->ldapAuthData->setFailed(true); return false; } @@ -116,36 +112,43 @@ final class LdapAuth implements LdapAuthInterface return true; } - /** - * Indica si es requerida para acceder a la aplicación - * - * @return boolean - */ - public function isAuthGranted(): bool - { - return !$this->configData->isLdapDatabaseEnabled(); - } - /** * Obtener los atributos del usuario. * - * @param string $userLogin + * @param string $userLogin * - * @return LdapAuthData con los atributos disponibles y sus valores + * @return void con los atributos disponibles y sus valores * @throws LdapException */ - public function getAttributes(string $userLogin): LdapAuthData + private function getAttributes(string $userLogin): void { - $attributes = $this->ldap->getLdapActions() - ->getAttributes($this->ldap->getUserDnFilter($userLogin)); + $filter = $this->ldap->getUserDnFilter($userLogin); + $attributes = $this->ldap->getLdapActions()->getAttributes($filter); + + if ($attributes->count() === 0) { + $this->eventDispatcher->notifyEvent( + 'ldap.getAttributes', + new Event( + $this, + EventMessage::factory()->addDescription(__u('Error while searching the user on LDAP'))->addDetail( + 'LDAP FILTER', + $filter + ) + ) + ); + + throw LdapException::error( + __u('Error while searching the user on LDAP'), + null, + LdapCodeEnum::NO_SUCH_OBJECT->value + ); + } if (!empty($attributes->get('fullname'))) { $this->ldapAuthData->setName($attributes->get('fullname')); } else { $name = trim( - $attributes->get('name', '') - .' ' - .$attributes->get('sn', '') + $attributes->get('name', '') . ' ' . $attributes->get('sn', '') ); $this->ldapAuthData->setName($name); @@ -167,7 +170,5 @@ final class LdapAuth implements LdapAuthInterface (array)$attributes->get('group') ) ); - - return $this->ldapAuthData; } } diff --git a/lib/SP/Providers/Auth/Ldap/LdapAuthData.php b/lib/SP/Providers/Auth/Ldap/LdapAuthData.php index 0802d746..0d29ba4b 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapAuthData.php +++ b/lib/SP/Providers/Auth/Ldap/LdapAuthData.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -33,84 +33,37 @@ use SP\Providers\Auth\AuthDataBase; */ final class LdapAuthData extends AuthDataBase { - /** - * @var string - */ - protected string $dn; - /** - * @var string - */ - protected string $groupDn; - /** - * @var int - */ - protected int $expire = 0; - /** - * @var bool - */ - protected bool $inGroup = false; + protected ?string $dn = null; + protected int $expire = 0; + protected bool $inGroup = false; - /** - * @return string - */ public function getDn(): ?string { return $this->dn; } - /** - * @param string $dn - */ public function setDn(string $dn): void { $this->dn = $dn; } - /** - * @return int - */ public function getExpire(): int { return $this->expire; } - /** - * @param int $expire - */ public function setExpire(int $expire): void { $this->expire = $expire; } - /** - * @return boolean - */ public function isInGroup(): bool { return $this->inGroup; } - /** - * @param boolean $inGroup - */ public function setInGroup(bool $inGroup): void { $this->inGroup = $inGroup; } - - /** - * @return string - */ - public function getGroupDn(): ?string - { - return $this->groupDn; - } - - /** - * @param string $groupDn - */ - public function setGroupDn(string $groupDn): void - { - $this->groupDn = $groupDn; - } -} \ No newline at end of file +} diff --git a/lib/SP/Providers/Auth/Ldap/LdapBase.php b/lib/SP/Providers/Auth/Ldap/LdapBase.php new file mode 100644 index 00000000..79339572 --- /dev/null +++ b/lib/SP/Providers/Auth/Ldap/LdapBase.php @@ -0,0 +1,140 @@ +. + */ + +namespace SP\Providers\Auth\Ldap; + +use Exception; +use SP\Core\Events\EventDispatcher; +use SP\Core\Events\EventDispatcherInterface; +use SP\Domain\Auth\Ports\LdapActionsInterface; +use SP\Domain\Auth\Ports\LdapConnectionInterface; +use SP\Domain\Auth\Ports\LdapInterface; + +use function SP\__u; + +/** + * Class LdapBase + * + * @package SP\Providers\Auth\Ldap + */ +abstract class LdapBase implements LdapInterface +{ + protected string $server; + + /** + * LdapBase constructor. + * + * @param LdapConnectionInterface $ldapConnection + * @param LdapActionsInterface $ldapActions + * @param LdapParams $ldapParams + * @param EventDispatcher $eventDispatcher + */ + public function __construct( + protected readonly LdapConnectionInterface $ldapConnection, + protected readonly LdapActionsInterface $ldapActions, + protected readonly LdapParams $ldapParams, + protected readonly EventDispatcherInterface $eventDispatcher + ) { + $this->server = $this->pickServer(); + } + + abstract protected function pickServer(): string; + + /** + * @param EventDispatcher $eventDispatcher + * @param LdapConnectionInterface $ldapConnection + * @param LdapActionsInterface $ldapActions + * @param LdapParams|null $ldapParams + * @return LdapInterface + * @throws LdapException + * @throws Exception + */ + public static function factory( + EventDispatcherInterface $eventDispatcher, + LdapConnectionInterface $ldapConnection, + LdapActionsInterface $ldapActions, + ?LdapParams $ldapParams = null + ): LdapInterface { + if (null !== $ldapParams) { + $ldapConnection = $ldapConnection->mutate($ldapParams); + $ldapActions = $ldapActions->mutate($ldapParams); + } + + $ldapConnection->checkConnection(); + + switch ($ldapParams->getType()) { + case LdapTypeEnum::STD: + return new LdapStd($ldapConnection, $ldapActions, $ldapParams, $eventDispatcher); + case LdapTypeEnum::ADS: + return new LdapMsAds($ldapConnection, $ldapActions, $ldapParams, $eventDispatcher); + case LdapTypeEnum::AZURE: + throw new LdapException(__u('To be implemented')); + } + + throw LdapException::error(__u('LDAP type not set')); + } + + public function getLdapActions(): LdapActionsInterface + { + return $this->ldapActions; + } + + /** + * @throws LdapException + */ + public function connect(?string $bindDn = null, ?string $bindPass = null): void + { + $this->ldapConnection->connect($bindDn, $bindPass); + } + + public function getServer(): string + { + return $this->server; + } + + /** + * @return string + */ + protected function getGroupFromParams(): string + { + if (stripos($this->ldapParams->getGroup(), 'cn') === 0) { + return LdapUtil::getGroupName($this->ldapParams->getGroup()) ?: ''; + } + + return $this->ldapParams->getGroup(); + } + + /** + * @return string + * @throws LdapException + */ + protected function getGroupDn(): string + { + if (stripos($this->ldapParams->getGroup(), 'cn') === 0) { + return $this->ldapParams->getGroup(); + } + + return $this->ldapActions->searchGroupsDn($this->getGroupObjectFilter())[0]; + } +} diff --git a/lib/SP/Providers/Auth/Ldap/LdapCode.php b/lib/SP/Providers/Auth/Ldap/LdapCodeEnum.php similarity index 62% rename from lib/SP/Providers/Auth/Ldap/LdapCode.php rename to lib/SP/Providers/Auth/Ldap/LdapCodeEnum.php index 61de34ce..830856ed 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapCode.php +++ b/lib/SP/Providers/Auth/Ldap/LdapCodeEnum.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -24,20 +24,17 @@ namespace SP\Providers\Auth\Ldap; - /** - * Interface LdapCode - * - * @package SP\Providers\Auth\Ldap + * Class LdapCodeEnum */ -interface LdapCode +enum LdapCodeEnum: int { - public const SUCCESS = 0; - public const OPERATIONS_ERROR = 1; - public const AUTH_METHOD_NOT_SUPPORTED = 7; - public const STRONGER_AUTH_REQUIRED = 8; - public const CONFIDENTIALITY_REQUIRED = 13; - public const NO_SUCH_OBJECT = 32; - public const INVALID_CREDENTIALS = 49; - public const FILTER_ERROR = 87; -} \ No newline at end of file + case SUCCESS = 0; + case OPERATIONS_ERROR = 1; + case AUTH_METHOD_NOT_SUPPORTED = 7; + case STRONGER_AUTH_REQUIRED = 8; + case CONFIDENTIALITY_REQUIRED = 13; + case NO_SUCH_OBJECT = 32; + case INVALID_CREDENTIALS = 49; + case FILTER_ERROR = 87; +} diff --git a/lib/SP/Providers/Auth/Ldap/LdapConnection.php b/lib/SP/Providers/Auth/Ldap/LdapConnection.php index 2aa59a7f..201052e2 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapConnection.php +++ b/lib/SP/Providers/Auth/Ldap/LdapConnection.php @@ -1,9 +1,10 @@ -setUp(); + } + + /** + * @throws LdapException + */ + private function setUp(): void { - $this->ldapParams = $ldapParams; - $this->eventDispatcher = $eventDispatcher; - $this->debug = $debug; + if ($this->debug) { + @ldap_set_option( + null, + LDAP_OPT_DEBUG_LEVEL, + 7 + ); + } + + try { + $this->ldap->setOptions([ + 'host' => $this->ldapParams->getPort(), + 'port' => $this->ldapParams->getPort(), + 'useStartTls' => $this->ldapParams->isTlsEnabled(), + 'username' => $this->ldapParams->getBindDn(), + 'password' => $this->ldapParams->getBindPass(), + 'networkTimeout' => self::TIMEOUT_SECONDS, + 'reconnectAttempts' => self::RECONNECT_ATTEMPTS, + ]); + } catch (LaminasLdapException $e) { + throw LdapException::error($e->getMessage(), null, $e->getCode(), $e); + } + } + + /** + * @throws LdapException + */ + public function mutate(LdapParams $ldapParams): LdapConnectionInterface + { + return new self($this->ldap, $ldapParams, $this->eventDispatcher, $this->debug); } /** @@ -76,264 +100,57 @@ final class LdapConnection implements LdapConnectionInterface */ public function checkConnection(): void { + $this->connect(); - $this->connectAndBind(); - - $this->eventDispatcher->notifyEvent('ldap.check.connection', - new Event($this, EventMessage::factory() - ->addDescription(__u('LDAP connection OK'))) + $this->eventDispatcher->notifyEvent( + 'ldap.check.connection', + new Event( + $this, + EventMessage::factory() + ->addDescription(__u('LDAP connection OK')) + ) ); } /** - * @return resource - * @throws LdapException - */ - public function connectAndBind() - { - if (!$this->isConnected && !$this->isBound) { - $this->isConnected = $this->connect(); - $this->isBound = $this->bind(); - } - - return $this->ldapHandler; - } - - /** - * Realizar la conexión al servidor de LDAP. + * Connects to LDAP server using authentication * - * @return bool - * @throws LdapException - */ - public function connect(): bool - { - if ($this->isConnected) { - return true; - } - - $this->checkParams(); - - // Habilitar la traza si el modo debug está habilitado - if ($this->debug) { - @ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7); - } - - $this->ldapHandler = @ldap_connect($this->getServerUri()); - - // Conexión al servidor LDAP - if (!is_resource($this->ldapHandler)) { - $this->eventDispatcher->notifyEvent('ldap.connect', - new Event($this, EventMessage::factory() - ->addDescription(__u('Unable to connect to LDAP server')) - ->addDetail(__u('Server'), $this->getServer())) - ); - - throw new LdapException(__u('Unable to connect to LDAP server')); - } - - @ldap_set_option($this->ldapHandler, LDAP_OPT_NETWORK_TIMEOUT, self::TIMEOUT); - @ldap_set_option($this->ldapHandler, LDAP_OPT_PROTOCOL_VERSION, 3); - @ldap_set_option($this->ldapHandler, LDAP_OPT_REFERRALS, 0); - - $this->isTls = $this->connectTls(); - - return true; - } - - /** - * Comprobar si los parámetros necesario de LDAP están establecidos. - * - * @throws LdapException - */ - public function checkParams(): bool - { - if (empty($this->ldapParams->getSearchBase()) - || empty($this->getServer()) - || empty($this->ldapParams->getBindDn()) - ) { - $this->eventDispatcher->notifyEvent('ldap.check.params', - new Event($this, EventMessage::factory() - ->addDescription(__u('LDAP parameters are not set')))); - - throw new LdapException(__u('LDAP parameters are not set')); - } - - return true; - } - - /** - * @return string - */ - public function getServer(): string - { - return $this->server ?: $this->ldapParams->getServer(); - } - - /** - * @param string $server - * - * @return LdapConnection - */ - public function setServer(string $server): LdapConnectionInterface - { - $this->server = $server; - - return $this; - } - - public function getServerUri(): string - { - $server = $this->getServer(); - $port = $this->ldapParams->getPort(); - - if (strpos($server, '://') !== false) { - return $server . ':' . $port; - } - - if ($port === 389) { - return 'ldap://' . $server; - } - - if ($port === 636) { - return 'ldaps://' . $server; - } - - return 'ldap://' . $server . ':' . $port; - } - - /** - * Connect through TLS - * - * @throws LdapException - */ - private function connectTls(): bool - { - if ($this->ldapParams->isTlsEnabled()) { - $result = @ldap_start_tls($this->ldapHandler); - - if ($result === false) { - $this->eventDispatcher->notifyEvent('ldap.connect.tls', - new Event($this, EventMessage::factory() - ->addDescription(__u('Unable to connect to LDAP server')) - ->addDetail(__u('Server'), $this->getServer()) - ->addDetail('TLS', __u('ON')) - ->addDetail('LDAP ERROR', self::getLdapErrorMessage($this->ldapHandler)))); - - throw new LdapException(__u('Unable to connect to LDAP server')); - } - - return true; - } - - return false; - } - - /** - * Registrar error de LDAP y devolver el mensaje de error - * - * @param resource $ldapHandler - * - * @return string - */ - public static function getLdapErrorMessage($ldapHandler): string - { - return sprintf('%s (%d)', ldap_error($ldapHandler), ldap_errno($ldapHandler)); - } - - /** - * Realizar la autentificación con el servidor de LDAP. - * - * @param string|null $bindDn con el DN del usuario + * @param string|null $bindDn con el DN del usuario * @param string|null $bindPass con la clave del usuario * - * @return bool * @throws LdapException */ - public function bind(string $bindDn = null, string $bindPass = null): bool - { - $dn = $bindDn ?: $this->ldapParams->getBindDn(); - $pass = $bindPass ?: $this->ldapParams->getBindPass(); + public function connect( + ?string $bindDn = null, + ?string $bindPass = null + ): void { + $username = $bindDn ?: $this->ldapParams->getBindDn(); + $password = $bindPass ?: $this->ldapParams->getBindPass(); - if (@ldap_bind($this->ldapHandler, $dn, $pass) === false) { - $this->eventDispatcher->notifyEvent('ldap.bind', - new Event($this, EventMessage::factory() - ->addDescription(__u('Connection error (BIND)')) - ->addDetail('LDAP ERROR', self::getLdapErrorMessage($this->ldapHandler)) - ->addDetail('LDAP DN', $dn)) + try { + $this->ldap->bind( + $username, + $password + ); + } catch (LaminasLdapException $e) { + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + + $this->eventDispatcher->notifyEvent( + 'ldap.bind', + new Event( + $this, + EventMessage::factory() + ->addDescription(__u('LDAP connection error')) + ->addDetail('LDAP ERROR', $this->ldap->getLastError()) + ->addDetail('LDAP DN', $username) + ) ); - throw new LdapException( - __u('Connection error (BIND)'), - SPException::ERROR, - self::getLdapErrorMessage($this->ldapHandler), - $this->getErrorCode() + throw LdapException::error( + __u('LDAP connection error'), + $this->ldap->getLastError(), + $this->ldap->getLastErrorCode() ); } - - return true; } - - /** - * @return int - */ - public function getErrorCode(): int - { - if (is_resource($this->ldapHandler)) { - return ldap_errno($this->ldapHandler); - } - - return -1; - } - - /** - * @return bool - */ - public function isConnected(): bool - { - return $this->isConnected; - } - - /** - * @return bool - */ - public function isBound(): bool - { - return $this->isBound; - } - - /** - * @return LdapParams - */ - public function getLdapParams(): LdapParams - { - return $this->ldapParams; - } - - /** - * @return bool - */ - public function isDebug(): bool - { - return $this->debug; - } - - /** - * Realizar la desconexión del servidor de LDAP. - */ - public function unbind(): bool - { - if (($this->isConnected || $this->isBound) - && @ldap_unbind($this->ldapHandler) === false - ) { - $this->eventDispatcher->notifyEvent('ldap.unbind', - new Event($this, EventMessage::factory() - ->addDescription(__u('Error while disconnecting from LDAP server')) - ->addDetail('LDAP ERROR', self::getLdapErrorMessage($this->ldapHandler))) - ); - - return false; - } - - return true; - } -} \ No newline at end of file +} diff --git a/lib/SP/Providers/Auth/Ldap/LdapConnectionInterface.php b/lib/SP/Providers/Auth/Ldap/LdapConnectionInterface.php deleted file mode 100644 index c8d698e8..00000000 --- a/lib/SP/Providers/Auth/Ldap/LdapConnectionInterface.php +++ /dev/null @@ -1,89 +0,0 @@ -. - */ - -namespace SP\Providers\Auth\Ldap; - -/** - * Interface LdapInterface - * - * @package Auth\Ldap - */ -interface LdapConnectionInterface -{ - /** - * Comprobar la conexión al servidor de LDAP. - */ - public function checkConnection(): void; - - /** - * Comprobar si los parámetros necesarios de LDAP están establecidos. - * - * @return bool - */ - public function checkParams(): bool; - - /** - * @return resource - * @throws LdapException - */ - public function connectAndBind(); - - /** - * Realizar la conexión al servidor de LDAP. - * - * @return bool - * @throws LdapException - */ - public function connect(): bool; - - /** - * @param string|null $bindDn - * @param string|null $bindPass - * - * @return bool - */ - public function bind(?string $bindDn = null, ?string $bindPass = null): bool; - - /** - * @return bool - */ - public function unbind(): bool; - - /** - * @return LdapParams - */ - public function getLdapParams(): LdapParams; - - /** - * @return string - */ - public function getServer(): string; - - /** - * @param string $server - * - * @return LdapConnectionInterface - */ - public function setServer(string $server): LdapConnectionInterface; -} \ No newline at end of file diff --git a/lib/SP/Providers/Auth/Ldap/LdapMsAds.php b/lib/SP/Providers/Auth/Ldap/LdapMsAds.php index a87d250a..bf0aa0ea 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapMsAds.php +++ b/lib/SP/Providers/Auth/Ldap/LdapMsAds.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -29,6 +29,9 @@ use SP\Core\Events\EventMessage; use SP\Core\Exceptions\SPException; use SP\Http\Address; +use function SP\__u; +use function SP\logger; + /** * Class LdapAds * @@ -36,11 +39,11 @@ use SP\Http\Address; * * @package SP\Auth\Ldap */ -final class LdapMsAds extends Ldap +final class LdapMsAds extends LdapBase { - public const DEFAULT_FILTER_USER_OBJECT = '(&(!(UserAccountControl:1.2.840.113556.1.4.804:=32))(|(objectCategory=person)(objectClass=user)))'; - public const DEFAULT_FILTER_GROUP_OBJECT = '(objectCategory=group)'; - public const DEFAULT_FILTER_USER_ATTRIBUTES = ['samaccountname', 'cn', 'uid', 'userPrincipalName']; + public const DEFAULT_FILTER_USER_OBJECT = '(&(!(UserAccountControl:1.2.840.113556.1.4.804:=32))(|(objectCategory=person)(objectClass=user)))'; + public const DEFAULT_FILTER_GROUP_OBJECT = '(objectCategory=group)'; + public const DEFAULT_FILTER_USER_ATTRIBUTES = ['samaccountname', 'cn', 'uid', 'userPrincipalName']; public const DEFAULT_FILTER_GROUP_ATTRIBUTES = ['memberOf', 'groupMembership', 'memberof:1.2.840.113556.1.4.1941:']; /** @@ -61,13 +64,7 @@ final class LdapMsAds extends Ldap $attributes = $this->ldapParams->getFilterGroupAttributes(); } - return '(&(|' - . LdapUtil::getAttributesForFilter( - $attributes, - $this->getGroupDn()) - . ')' - . $filter - . ')'; + return '(&(|' . LdapUtil::getAttributesForFilter($attributes, $this->getGroupDn()) . ')' . $filter . ')'; } /** @@ -94,10 +91,10 @@ final class LdapMsAds extends Ldap } return '(&(|' - . LdapUtil::getAttributesForFilter($attributes, $userLogin) - . ')' - . $this->getUserObjectFilter() - . ')'; + . LdapUtil::getAttributesForFilter($attributes, $userLogin) + . ')' + . $this->getUserObjectFilter() + . ')'; } /** @@ -124,11 +121,16 @@ final class LdapMsAds extends Ldap || $this->ldapParams->getGroup() === '*' || in_array($this->getGroupDn(), $groupsDn, true) ) { - $this->eventDispatcher->notifyEvent('ldap.check.group', - new Event($this, EventMessage::factory() - ->addDescription(__u('User in group verified')) - ->addDetail(__u('User'), $userLogin) - ->addDetail(__u('Group'), $this->ldapParams->getGroup()))); + $this->eventDispatcher->notifyEvent( + 'ldap.check.group', + new Event( + $this, + EventMessage::factory() + ->addDescription(__u('User in group verified')) + ->addDetail(__u('User'), $userLogin) + ->addDetail(__u('Group'), $this->ldapParams->getGroup()) + ) + ); return true; } @@ -153,21 +155,31 @@ final class LdapMsAds extends Ldap if (isset($searchResults['count']) && (int)$searchResults['count'] === 0 ) { - $this->eventDispatcher->notifyEvent('ldap.check.group', - new Event($this, EventMessage::factory() - ->addDescription(__u('User does not belong to the group')) - ->addDetail(__u('User'), $userLogin) - ->addDetail(__u('Group'), $groupDn) - ->addDetail('LDAP FILTER', $filter))); + $this->eventDispatcher->notifyEvent( + 'ldap.check.group', + new Event( + $this, + EventMessage::factory() + ->addDescription(__u('User does not belong to the group')) + ->addDetail(__u('User'), $userLogin) + ->addDetail(__u('Group'), $groupDn) + ->addDetail('LDAP FILTER', $filter) + ) + ); return false; } - $this->eventDispatcher->notifyEvent('ldap.check.group', - new Event($this, EventMessage::factory() - ->addDescription(__u('User in group verified')) - ->addDetail(__u('User'), $userLogin) - ->addDetail(__u('Group'), $groupDn))); + $this->eventDispatcher->notifyEvent( + 'ldap.check.group', + new Event( + $this, + EventMessage::factory() + ->addDescription(__u('User in group verified')) + ->addDetail(__u('User'), $userLogin) + ->addDetail(__u('Group'), $groupDn) + ) + ); return true; } @@ -185,13 +197,10 @@ final class LdapMsAds extends Ldap } return '(|' - . LdapUtil::getAttributesForFilter($attributes, $this->getGroupDn()) - . ')'; + . LdapUtil::getAttributesForFilter($attributes, $this->getGroupDn()) + . ')'; } - /** - * @inheritDoc - */ protected function pickServer(): string { $server = $this->ldapParams->getServer(); diff --git a/lib/SP/Providers/Auth/Ldap/LdapParams.php b/lib/SP/Providers/Auth/Ldap/LdapParams.php index b826a032..a57fdc65 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapParams.php +++ b/lib/SP/Providers/Auth/Ldap/LdapParams.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -27,6 +27,8 @@ namespace SP\Providers\Auth\Ldap; use SP\Core\Exceptions\ValidationException; use SP\Domain\Config\Ports\ConfigDataInterface; +use function SP\__u; + /** * Class LdapParams * @@ -36,74 +38,44 @@ final class LdapParams { private const REGEX_SERVER = '(?(?:(?ldap|ldaps):\/\/)?[\w\.\-]+)(?::(?\d+))?'; - /** - * @var string - */ - protected string $server; - /** - * @var int - */ - protected int $port = 389; - /** - * @var string - */ - protected string $searchBase; - /** - * @var string - */ - protected string $bindDn; - /** - * @var string - */ - protected string $bindPass; - /** - * @var string - */ - protected string $group; - /** - * @var int - */ - protected int $type; - /** - * @var bool - */ - protected bool $tlsEnabled = false; - /** - * @var string - */ - protected string $filterUserObject; - /** - * @var string - */ - protected string $filterGroupObject; - /** - * @var array - */ - protected array $filterUserAttributes; - /** - * @var array - */ - protected array $filterGroupAttributes; + private int $port = 389; + private ?string $searchBase = null; + private ?string $group = null; + private bool $tlsEnabled = false; + private ?string $filterUserObject = null; + private ?string $filterGroupObject = null; + private ?array $filterUserAttributes = null; + private ?array $filterGroupAttributes = null; + + public function __construct( + private readonly string $server, + private readonly LdapTypeEnum $type, + private readonly string $bindDn, + private readonly string $bindPass + ) { + } /** - * @throws \SP\Core\Exceptions\ValidationException + * @throws ValidationException */ public static function getFrom(ConfigDataInterface $configData): LdapParams { $data = self::getServerAndPort($configData->getLdapServer()); if (count($data) === 0) { - throw new ValidationException(__u('Wrong LDAP parameters')); + throw ValidationException::error(__u('Wrong LDAP parameters')); } - $ldapParams = new self(); - $ldapParams->setServer($data['server']); + $ldapParams = new self( + $data['server'], + LdapTypeEnum::from($configData->getLdapType()), + $configData->getLdapBindUser(), + $configData->getLdapBindPass() + ); + $ldapParams->setPort($data['port'] ?? 389); $ldapParams->setSearchBase($configData->getLdapBase()); $ldapParams->setGroup($configData->getLdapGroup()); - $ldapParams->setBindDn($configData->getLdapBindUser()); - $ldapParams->setBindPass($configData->getLdapBindPass()); - $ldapParams->setType($configData->getLdapType()); $ldapParams->setFilterUserObject($configData->getLdapFilterUserObject()); $ldapParams->setFilterGroupObject($configData->getLdapFilterGroupObject()); $ldapParams->setFilterUserAttributes($configData->getLdapFilterUserAttributes()); @@ -122,23 +94,17 @@ final class LdapParams public static function getServerAndPort($server): array { return preg_match( - '#'.self::REGEX_SERVER.'#i', + '#' . self::REGEX_SERVER . '#i', $server, $matches ) ? $matches : []; } - /** - * @return string - */ public function getFilterUserObject(): ?string { return $this->filterUserObject; } - /** - * @param string|null $filterUserObject - */ public function setFilterUserObject(?string $filterUserObject = null): void { if (!empty($filterUserObject)) { @@ -146,17 +112,11 @@ final class LdapParams } } - /** - * @return string - */ public function getFilterGroupObject(): ?string { return $this->filterGroupObject; } - /** - * @param string|null $filterGroupObject - */ public function setFilterGroupObject(?string $filterGroupObject = null): void { if (!empty($filterGroupObject)) { @@ -164,51 +124,31 @@ final class LdapParams } } - /** - * @return array - */ public function getFilterUserAttributes(): ?array { return $this->filterUserAttributes; } - /** - * @param array|null $filterUserAttributes - */ public function setFilterUserAttributes(?array $filterUserAttributes = null): void { $this->filterUserAttributes = $filterUserAttributes; } - /** - * @return array - */ public function getFilterGroupAttributes(): ?array { return $this->filterGroupAttributes; } - /** - * @param array|null $filterGroupAttributes - */ public function setFilterGroupAttributes(?array $filterGroupAttributes = null): void { $this->filterGroupAttributes = $filterGroupAttributes; } - /** - * @return int - */ public function getPort(): int { return $this->port; } - /** - * @param int $port - * - * @return LdapParams - */ public function setPort(int $port): LdapParams { $this->port = $port; @@ -216,19 +156,11 @@ final class LdapParams return $this; } - /** - * @return string - */ public function getSearchBase(): ?string { return $this->searchBase; } - /** - * @param string $searchBase - * - * @return LdapParams - */ public function setSearchBase(string $searchBase): LdapParams { $this->searchBase = $searchBase; @@ -236,59 +168,21 @@ final class LdapParams return $this; } - /** - * @return string - */ public function getBindDn(): ?string { return $this->bindDn; } - /** - * @param string $bindDn - * - * @return LdapParams - */ - public function setBindDn(string $bindDn): LdapParams - { - $this->bindDn = $bindDn; - - return $this; - } - - /** - * @return string - */ public function getBindPass(): ?string { return $this->bindPass; } - /** - * @param string $bindPass - * - * @return LdapParams - */ - public function setBindPass(string $bindPass): LdapParams - { - $this->bindPass = $bindPass; - - return $this; - } - - /** - * @return string - */ public function getGroup(): ?string { return $this->group; } - /** - * @param string $group - * - * @return LdapParams - */ public function setGroup(string $group): LdapParams { $this->group = $group; @@ -296,59 +190,21 @@ final class LdapParams return $this; } - /** - * @return string|null - */ public function getServer(): ?string { return $this->server; } - /** - * @param string $server - * - * @return LdapParams - */ - public function setServer(string $server): LdapParams - { - $this->server = $server; - - return $this; - } - - /** - * @return int - */ - public function getType(): ?int + public function getType(): LdapTypeEnum { return $this->type; } - /** - * @param int $type - * - * @return LdapParams - */ - public function setType(int $type): LdapParams - { - $this->type = $type; - - return $this; - } - - /** - * @return bool - */ public function isTlsEnabled(): bool { return $this->tlsEnabled; } - /** - * @param bool $tlsEnabled - * - * @return LdapParams - */ public function setTlsEnabled(bool $tlsEnabled): LdapParams { $this->tlsEnabled = $tlsEnabled; diff --git a/lib/SP/Providers/Auth/Ldap/LdapStd.php b/lib/SP/Providers/Auth/Ldap/LdapStd.php index cf14806c..680a7baf 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapStd.php +++ b/lib/SP/Providers/Auth/Ldap/LdapStd.php @@ -1,9 +1,10 @@ -ldapParams->getFilterGroupAttributes(); } - return '(&(|' - . LdapUtil::getAttributesForFilter( - $attributes, - $this->getGroupDn()) - . ')' . $filter - . ')'; + return '(&(|' . LdapUtil::getAttributesForFilter($attributes, $this->getGroupDn()) . ')' . $filter . ')'; } /** @@ -93,11 +91,7 @@ final class LdapStd extends Ldap $filter = $this->getUserObjectFilter(); - return '(&(|' - . LdapUtil::getAttributesForFilter($attributes, $userLogin) - . ')' - . $filter - . ')'; + return '(&(|' . LdapUtil::getAttributesForFilter($attributes, $userLogin) . ')' . $filter . ')'; } /** @@ -110,13 +104,20 @@ final class LdapStd extends Ldap // los grupos del usuario if (empty($this->ldapParams->getGroup()) || $this->ldapParams->getGroup() === '*' - || in_array($this->getGroupDn(), $groupsDn, true) - ) { - $this->eventDispatcher->notifyEvent('ldap.check.group', - new Event($this, EventMessage::factory() - ->addDescription(__u('User in group verified')) - ->addDetail(__u('User'), $userDn) - ->addDetail(__u('Group'), $this->ldapParams->getGroup()))); + || in_array($this->getGroupDn(), $groupsDn, true)) { + $this->eventDispatcher->notifyEvent( + 'ldap.check.group', + new Event( + $this, + EventMessage::factory() + ->addDescription(__u('User in group verified')) + ->addDetail( + __u('User'), + $userDn + ) + ->addDetail(__u('Group'), $this->ldapParams->getGroup()) + ) + ); return true; } @@ -136,15 +137,24 @@ final class LdapStd extends Ldap $searchResults = $this->ldapActions->getObjects($filter, ['dn']); - if (isset($searchResults['count']) - && (int)$searchResults['count'] === 0 - ) { - $this->eventDispatcher->notifyEvent('ldap.check.group', - new Event($this, EventMessage::factory() - ->addDescription(__u('User does not belong to the group')) - ->addDetail(__u('User'), $userDn) - ->addDetail(__u('Group'), $this->getGroupFromParams()) - ->addDetail('LDAP FILTER', $filter))); + if (isset($searchResults['count']) && (int)$searchResults['count'] === 0) { + $this->eventDispatcher->notifyEvent( + 'ldap.check.group', + new Event( + $this, + EventMessage::factory() + ->addDescription(__u('User does not belong to the group')) + ->addDetail( + __u('User'), + $userDn + ) + ->addDetail(__u('Group'), $this->getGroupFromParams()) + ->addDetail( + 'LDAP FILTER', + $filter + ) + ) + ); return false; } @@ -164,10 +174,8 @@ final class LdapStd extends Ldap return $this->getUserObjectFilter(); } - return '(&(cn=' . $groupName . ')' - . '(|(memberUid=' . $member . ')(member=' . $member . ')(uniqueMember=' . $member . '))' - . $this->getGroupObjectFilter() - . ')'; + return '(&(cn=' . $groupName . ')' . '(|(memberUid=' . $member . ')(member=' . $member . ')(uniqueMember=' . $member . '))' . + $this->getGroupObjectFilter() . ')'; } /** @@ -191,4 +199,4 @@ final class LdapStd extends Ldap { return $this->ldapParams->getServer(); } -} \ No newline at end of file +} diff --git a/lib/SP/Providers/Auth/Ldap/LdapTypeInterface.php b/lib/SP/Providers/Auth/Ldap/LdapTypeEnum.php similarity index 75% rename from lib/SP/Providers/Auth/Ldap/LdapTypeInterface.php rename to lib/SP/Providers/Auth/Ldap/LdapTypeEnum.php index 1f4566f6..0802372e 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapTypeInterface.php +++ b/lib/SP/Providers/Auth/Ldap/LdapTypeEnum.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -24,15 +24,12 @@ namespace SP\Providers\Auth\Ldap; - /** - * Interface LdapTypeInterface - * - * @package SP\Providers\Auth\Ldap + * Class LdapTypeEnum */ -interface LdapTypeInterface +enum LdapTypeEnum: int { - public const LDAP_STD = 1; - public const LDAP_ADS = 2; - public const LDAP_AZURE = 3; -} \ No newline at end of file + case STD = 1; + case ADS = 2; + case AZURE = 3; +} diff --git a/lib/SP/Providers/Auth/Ldap/LdapUtil.php b/lib/SP/Providers/Auth/Ldap/LdapUtil.php index fa5a4bb8..086288f9 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapUtil.php +++ b/lib/SP/Providers/Auth/Ldap/LdapUtil.php @@ -1,9 +1,10 @@ -\+#\/]+)/', + '/([";<>+#\/]+)/', '/\G(\s)/', - '/(\s)(?=\s*$)/' + '/(\s)(?=\s*$)/', ]; return preg_replace($chars, '\\\1', $dn); @@ -57,23 +58,19 @@ final class LdapUtil * * @param string $group * - * @return bool|string + * @return string|null */ - public static function getGroupName(string $group) + public static function getGroupName(string $group): ?string { - if (preg_match( - '/^cn=(?[^,]+),.*/i', - $group, - $matches) - ) { + if (preg_match('/^cn=(?[^,]+),.*/i', $group, $matches)) { return $matches['groupname']; } - return false; + return null; } /** - * @param array $attributes + * @param array $attributes * @param string $value * * @return string @@ -92,4 +89,4 @@ final class LdapUtil ) ); } -} \ No newline at end of file +} diff --git a/tests/SP/Html/HtmlTest.php b/tests/SP/Html/HtmlTest.php index bab9d18d..6e651680 100644 --- a/tests/SP/Html/HtmlTest.php +++ b/tests/SP/Html/HtmlTest.php @@ -24,23 +24,16 @@ namespace SP\Tests\Html; -use Faker\Factory; -use PHPUnit\Framework\TestCase; use SP\Html\Html; +use SP\Tests\UnitaryTestCase; /** * Class HtmlTest + * + * @group unitary */ -class HtmlTest extends TestCase +class HtmlTest extends UnitaryTestCase { - private static $faker; - - public static function setUpBeforeClass(): void - { - parent::setUpBeforeClass(); - - self::$faker = Factory::create(); - } public static function urlProvider(): array { diff --git a/tests/SP/Http/AddressTest.php b/tests/SP/Http/AddressTest.php index 82452a49..a902b572 100644 --- a/tests/SP/Http/AddressTest.php +++ b/tests/SP/Http/AddressTest.php @@ -24,31 +24,21 @@ namespace SP\Tests\Http; -use Faker\Factory; -use PHPUnit\Framework\TestCase; use SP\Core\Exceptions\InvalidArgumentException; use SP\Http\Address; +use SP\Tests\UnitaryTestCase; /** * Class AddressTest * - * @package SP\Tests\Http + * @group unitary */ -class AddressTest extends TestCase +class AddressTest extends UnitaryTestCase { public static function binaryCheckProvider(): array { - $faker = Factory::create(); - - $out = []; - - for ($i = 0; $i <= 100; $i++) { - $out[] = [$faker->ipv4]; - $out[] = [$faker->ipv6]; - } - - return $out; + return array_map(fn() => [[self::$faker->ipv4], [self::$faker->ipv6]], range(0, 99)); } public static function checkAddressProvider(): array @@ -121,7 +111,7 @@ class AddressTest extends TestCase * * @throws InvalidArgumentException */ - public function testBinary($address) + public function testBinary(string $address) { $binary = Address::toBinary($address); @@ -159,7 +149,7 @@ class AddressTest extends TestCase * * @throws InvalidArgumentException */ - public function testCheck($address, $inAddress, $inMask, $expected) + public function testCheck(string $address, string $inAddress, string $inMask, bool $expected) { $this->assertEquals($expected, Address::check($address, $inAddress, $inMask)); } @@ -174,7 +164,7 @@ class AddressTest extends TestCase * * @throws InvalidArgumentException */ - public function testCheckWithCidr($address, $inAddress, $inMask, $expected) + public function testCheckWithCidr(string $address, string $inAddress, string $inMask, bool $expected) { $this->assertEquals($expected, Address::check($address, $inAddress, Address::cidrToDec($inMask))); } @@ -182,7 +172,7 @@ class AddressTest extends TestCase /** * @throws InvalidArgumentException */ - public function testParse() + public function testParseWithFullMask() { $address = '192.168.0.1/255.255.255.0'; $parse = Address::parse4($address); @@ -192,7 +182,13 @@ class AddressTest extends TestCase $this->assertEquals('192.168.0.1', $parse['address']); $this->assertArrayHasKey('mask', $parse); $this->assertEquals('255.255.255.0', $parse['mask']); + } + /** + * @throws InvalidArgumentException + */ + public function testParseWithoutMask() + { $address = '192.168.0.2'; $parse = Address::parse4($address); @@ -210,6 +206,21 @@ class AddressTest extends TestCase $this->assertEquals('24', $parse['cidr']); } + /** + * @throws InvalidArgumentException + */ + public function testParseWithCIDR() + { + $address = '192.168.0.1/24'; + $parse = Address::parse4($address); + + $this->assertCount(7, $parse); + $this->assertArrayHasKey('address', $parse); + $this->assertEquals('192.168.0.1', $parse['address']); + $this->assertArrayHasKey('cidr', $parse); + $this->assertEquals('24', $parse['cidr']); + } + /** * @throws InvalidArgumentException */ diff --git a/tests/SP/Providers/Auth/Ldap/LdapActionsTest.php b/tests/SP/Providers/Auth/Ldap/LdapActionsTest.php new file mode 100644 index 00000000..1946b375 --- /dev/null +++ b/tests/SP/Providers/Auth/Ldap/LdapActionsTest.php @@ -0,0 +1,285 @@ +. + */ + +namespace SP\Tests\Providers\Auth\Ldap; + +use Laminas\Ldap\Collection; +use Laminas\Ldap\Ldap; +use PHPUnit\Framework\MockObject\Exception; +use PHPUnit\Framework\MockObject\MockObject; +use SP\Core\Events\Event; +use SP\Core\Events\EventDispatcherInterface; +use SP\Providers\Auth\Ldap\AttributeCollection; +use SP\Providers\Auth\Ldap\LdapActions; +use SP\Providers\Auth\Ldap\LdapCodeEnum; +use SP\Providers\Auth\Ldap\LdapException; +use SP\Providers\Auth\Ldap\LdapParams; +use SP\Providers\Auth\Ldap\LdapTypeEnum; +use SP\Tests\UnitaryTestCase; + +/** + * Class LdapActionsTest + * + * @group unitary + */ +class LdapActionsTest extends UnitaryTestCase +{ + + private Ldap|MockObject $ldap; + private EventDispatcherInterface|MockObject $eventDispatcher; + private LdapActions $ldapActions; + + /** + * @throws LdapException + * @throws Exception + */ + public function testGetObjects(): void + { + $filter = 'test'; + $collection = $this->createMock(Collection::class); + $result = array_map(fn() => self::$faker->randomNumber(), range(0, 9)); + $attributes = array_map(fn() => self::$faker->colorName, range(0, 9)); + $searchBase = self::$faker->colorName; + + $this->ldap->expects(self::once()) + ->method('search') + ->with( + $filter, + $searchBase, + Ldap::SEARCH_SCOPE_SUB, + $attributes, + ) + ->willReturn($collection); + + $collection->expects(self::once())->method('toArray')->willReturn($result); + + $out = $this->ldapActions->getObjects($filter, $attributes, $searchBase); + + self::assertEquals($result, $out); + } + + /** + * @throws LdapException + */ + public function testGetObjectsError(): void + { + $this->expectGetResultsError(); + + $this->ldapActions->getObjects('test'); + } + + /** + * @return void + */ + private function expectGetResultsError(): void + { + $message = 'test'; + $code = self::$faker->randomNumber(); + $exception = new \Laminas\Ldap\Exception\LdapException(null, $message, $code); + + $this->ldap->expects(self::once()) + ->method('search') + ->willThrowException($exception); + + $this->eventDispatcher->expects(self::once()) + ->method('notifyEvent') + ->with('exception', new Event($exception)); + + $this->expectException(LdapException::class); + $this->expectExceptionMessage($message); + $this->expectExceptionCode($code); + } + + /** + * @throws LdapException + * @throws Exception + */ + public function testGetAttributes(): void + { + $filter = 'test'; + $collection = $this->createMock(Collection::class); + $attributes = $this->buildAttributes(); + + $this->ldap->expects(self::once()) + ->method('search') + ->with( + $filter, + null, + Ldap::SEARCH_SCOPE_SUB, + [], + ) + ->willReturn($collection); + + $collection->expects(self::once())->method('getFirst')->willReturn($attributes); + + $out = $this->ldapActions->getAttributes($filter); + + $expected = new AttributeCollection([ + 'dn' => $attributes['dn'], + 'group' => array_filter( + $attributes['memberof'], + fn($key) => $key !== 'count', + ARRAY_FILTER_USE_KEY + ), + 'fullname' => $attributes['displayname'], + 'name' => $attributes['givenname'], + 'sn' => $attributes['sn'], + 'mail' => $attributes['mail'], + 'expire' => $attributes['lockouttime'], + ]); + + self::assertEquals($expected, $out); + } + + /** + * @return array + */ + private function buildAttributes(): array + { + return [ + 'dn' => self::$faker->userName, + 'memberof' => [ + 'count' => 3, + self::$faker->company, + self::$faker->company, + self::$faker->company, + ], + 'displayname' => self::$faker->name, + 'givenname' => self::$faker->firstName, + 'sn' => self::$faker->lastName, + 'mail' => self::$faker->email, + 'lockouttime' => self::$faker->unixTime, + ]; + } + + /** + * @throws \SP\Providers\Auth\Ldap\LdapException + */ + public function testGetAttributesError(): void + { + $this->expectGetResultsError(); + + $this->ldapActions->getAttributes('test'); + } + + public function testMutate(): void + { + $ldapParams = + new LdapParams(self::$faker->domainName, LdapTypeEnum::ADS, self::$faker->company, self::$faker->password); + + $this->ldapActions->mutate($ldapParams); + + $this->assertTrue(true); + } + + /** + * @throws \SP\Providers\Auth\Ldap\LdapException + * @throws \PHPUnit\Framework\MockObject\Exception + */ + public function testSearchGroupsDn(): void + { + $filter = 'test'; + $collection = $this->createMock(Collection::class); + + $this->ldap->expects(self::once()) + ->method('search') + ->with( + '(&(cn=\2a)test)', + null, + Ldap::SEARCH_SCOPE_SUB, + ['dn'], + ) + ->willReturn($collection); + + $collection->expects(self::once())->method('count')->willReturn(1); + + $expected = [ + [], + [ + 'dn' => self::$faker->name, + ], + ]; + $collection->expects(self::once())->method('toArray')->willReturn($expected); + + $out = $this->ldapActions->searchGroupsDn($filter); + + self::assertEquals($expected[1]['dn'], $out[0]); + } + + /** + * @throws \SP\Providers\Auth\Ldap\LdapException + * @throws \PHPUnit\Framework\MockObject\Exception + */ + public function testSearchGroupsDnNoGroups(): void + { + $filter = 'test'; + $collection = $this->createMock(Collection::class); + + $this->ldap->expects(self::once()) + ->method('search') + ->with( + '(&(cn=\2a)test)', + null, + Ldap::SEARCH_SCOPE_SUB, + ['dn'], + ) + ->willReturn($collection); + + $this->eventDispatcher->expects(self::once()) + ->method('notifyEvent') + ->with('ldap.search.group'); + + $collection->expects(self::once())->method('count')->willReturn(0); + + $this->expectException(LdapException::class); + $this->expectExceptionMessage('Error while searching the group RDN'); + $this->expectExceptionCode(LdapCodeEnum::NO_SUCH_OBJECT->value); + + $this->ldapActions->searchGroupsDn($filter); + } + + /** + * @throws \SP\Providers\Auth\Ldap\LdapException + */ + public function testSearchGroupsDnError(): void + { + $this->expectGetResultsError(); + + $this->ldapActions->searchGroupsDn('test'); + } + + protected function setUp(): void + { + parent::setUp(); + + $this->ldap = $this->createMock(Ldap::class); + $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class); + + $ldapParams = + new LdapParams(self::$faker->domainName, LdapTypeEnum::STD, self::$faker->userName, self::$faker->password); + + $this->ldapActions = new LdapActions($this->ldap, $ldapParams, $this->eventDispatcher); + } + +} diff --git a/tests/SP/Providers/Auth/Ldap/LdapConnectionTest.php b/tests/SP/Providers/Auth/Ldap/LdapConnectionTest.php index eea7b848..92b8d5df 100644 --- a/tests/SP/Providers/Auth/Ldap/LdapConnectionTest.php +++ b/tests/SP/Providers/Auth/Ldap/LdapConnectionTest.php @@ -1,10 +1,10 @@ . + * along with sysPass. If not, see . */ namespace SP\Tests\Providers\Auth\Ldap; -use PHPUnit\Framework\TestCase; -use SP\Core\Events\EventDispatcher; +use Laminas\Ldap\Ldap; +use PHPUnit\Framework\MockObject\MockObject; +use SP\Core\Events\EventDispatcherInterface; use SP\Providers\Auth\Ldap\LdapConnection; use SP\Providers\Auth\Ldap\LdapException; use SP\Providers\Auth\Ldap\LdapParams; -use SP\Providers\Auth\Ldap\LdapTypeInterface; +use SP\Providers\Auth\Ldap\LdapTypeEnum; +use SP\Tests\UnitaryTestCase; +use function PHPUnit\Framework\once; /** * Class LdapConnectionTest * - * @package SP\Tests\Providers\Auth\Ldap + * @group unitary */ -class LdapConnectionTest extends TestCase +class LdapConnectionTest extends UnitaryTestCase { + private LdapConnection $ldapConnection; + private EventDispatcherInterface|MockObject $eventDispatcher; + private Ldap|MockObject $ldap; + private LdapParams $ldapParams; + /** - * @throws LdapException + * @throws \SP\Providers\Auth\Ldap\LdapException */ - public function testCheckParams() + public function testCheckConnection(): void { - $ldapConnection = $this->getLdapConnection(); + $this->ldap + ->expects(self::once()) + ->method('bind') + ->with($this->ldapParams->getBindDn(), $this->ldapParams->getBindPass()); - $ldapConnection->checkParams(); + $this->eventDispatcher + ->expects(once()) + ->method('notifyEvent') + ->with('ldap.check.connection'); - $this->assertTrue(true); + $this->ldapConnection->checkConnection(); } /** - * @param LdapParams|null $params - * - * @return LdapConnection + * @throws \SP\Providers\Auth\Ldap\LdapException */ - public function getLdapConnection(LdapParams $params = null) + public function testCheckConnectionError(): void { - $ev = new EventDispatcher(); + $this->expectConnectError(); - if ($params === null) { - $params = new LdapParams(); - $params->setServer('test.example.com'); - $params->setPort(10389); - $params->setBindDn('cn=test,dc=example,dc=com'); - $params->setBindPass('testpass'); - $params->setGroup('cn=Test Group,ou=Groups,dc=example,dc=con'); - $params->setSearchBase('dc=example,dc=com'); - $params->setTlsEnabled(true); - $params->setType(LdapTypeInterface::LDAP_STD); - } - - return new LdapConnection($params, $ev); + $this->ldapConnection->checkConnection(); } /** - * @throws LdapException + * @return void */ - public function testCheckParamsNoSearchBase() + private function expectConnectError(): void { - $ldapConnection = $this->getLdapConnection(); + $this->ldap + ->expects(self::once()) + ->method('bind') + ->with($this->ldapParams->getBindDn(), $this->ldapParams->getBindPass()) + ->willThrowException(new \Laminas\Ldap\Exception\LdapException()); - $params = $ldapConnection->getLdapParams(); - $params->setSearchBase(''); + $this->eventDispatcher + ->expects(self::exactly(2)) + ->method('notifyEvent') + ->with(...self::withConsecutive(['exception'], ['ldap.bind'])); + + $this->ldap + ->expects(self::exactly(2)) + ->method('getLastError') + ->willReturn('error'); + + $errorCode = self::$faker->randomNumber(); + + $this->ldap + ->expects(self::once()) + ->method('getLastErrorCode') + ->willReturn($errorCode); $this->expectException(LdapException::class); - $ldapConnection->checkParams(); + $this->expectExceptionMessage('LDAP connection error'); + $this->expectExceptionCode($errorCode); } /** - * @throws LdapException + * @throws \SP\Providers\Auth\Ldap\LdapException */ - public function testCheckParamsNoServer() + public function testConnect(): void { - $ldapConnection = $this->getLdapConnection(); + $this->ldap + ->expects(self::once()) + ->method('bind') + ->with($this->ldapParams->getBindDn(), $this->ldapParams->getBindPass()); - $params = $ldapConnection->getLdapParams(); - $params->setServer(''); - - $this->expectException(LdapException::class); - $ldapConnection->checkParams(); + $this->ldapConnection->connect(); } /** - * @throws LdapException + * @throws \SP\Providers\Auth\Ldap\LdapException */ - public function testCheckParamsNoBindDn() + public function testConnectError(): void { - $ldapConnection = $this->getLdapConnection(); + $this->expectConnectError(); - $params = $ldapConnection->getLdapParams(); - $params->setBindDn(''); + $this->ldapConnection->connect(); + } + + /** + * @throws \SP\Providers\Auth\Ldap\LdapException + */ + public function testMutate(): void + { + $ldapParams = new LdapParams( + self::$faker->domainName, + LdapTypeEnum::STD, + 'cn=test1,dc=example,dc=com', + self::$faker->password + ); + + $ldapConnection = $this->ldapConnection->mutate($ldapParams); + + $this->ldap + ->expects(self::once()) + ->method('bind') + ->with($ldapParams->getBindDn(), $ldapParams->getBindPass()); + + $ldapConnection->connect(); + } + + /** + * @throws \SP\Providers\Auth\Ldap\LdapException + */ + public function testCreateInstanceError(): void + { + $message = self::$faker->colorName; + $errorCode = self::$faker->randomNumber(); + + $this->ldap + ->expects(self::once()) + ->method('setOptions') + ->willThrowException( + new \Laminas\Ldap\Exception\LdapException(null, $message, $errorCode) + ); $this->expectException(LdapException::class); - $ldapConnection->checkParams(); + $this->expectExceptionMessage($message); + $this->expectExceptionCode($errorCode); + + new LdapConnection($this->ldap, $this->ldapParams, $this->eventDispatcher, true); } - public function testGetServerUri() + /** + * @throws \PHPUnit\Framework\MockObject\Exception + * @throws \SP\Core\Context\ContextException + * @throws \SP\Providers\Auth\Ldap\LdapException + */ + protected function setUp(): void { - $ldapConnection = $this->getLdapConnection(); + parent::setUp(); - $this->assertEquals('ldap://test.example.com:10389', $ldapConnection->getServerUri()); + $this->ldapParams = new LdapParams( + self::$faker->domainName, + LdapTypeEnum::STD, + 'cn=test,dc=example,dc=com', + self::$faker->password + ); + $this->ldapParams->setPort(10389); + $this->ldapParams->setGroup('cn=Test Group,ou=Groups,dc=example,dc=con'); + $this->ldapParams->setSearchBase('dc=example,dc=com'); + $this->ldapParams->setTlsEnabled(true); + + $this->ldap = $this->createMock(Ldap::class); + $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class); + + $this->ldapConnection = + new LdapConnection($this->ldap, $this->ldapParams, $this->eventDispatcher, true); } - public function testGetServerUriNoSchema() - { - $ldapConnection = $this->getLdapConnection(); - - $params = $ldapConnection->getLdapParams(); - $params->setServer('test.example.com'); - $params->setPort(389); - - $this->assertEquals('ldap://test.example.com', $ldapConnection->getServerUri()); - - $params->setPort(10389); - $this->assertEquals('ldap://test.example.com:10389', $ldapConnection->getServerUri()); - } - - public function testGetServerUriLdaps() - { - $ldapConnection = $this->getLdapConnection(); - - $params = $ldapConnection->getLdapParams(); - $params->setServer('ldaps://test.example.com'); - $params->setPort(10636); - - $this->assertEquals('ldaps://test.example.com:10636', $ldapConnection->getServerUri()); - } } diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 7a66b89e..b24925a4 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -70,7 +70,6 @@ ../lib/SP/DataModel - ../lib/SP/Providers ../lib/SP/Html/Assets ../lib/SP/Html/DataGrid ../lib/SP/Config/ConfigData.php