Add Codestyle definition, editorconfig
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
23af542178
commit
75b65f3f77
34 changed files with 1300 additions and 1235 deletions
9
.editorconfig
Normal file
9
.editorconfig
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
380
fling-java-codestyle.xml
Normal file
380
fling-java-codestyle.xml
Normal file
|
@ -0,0 +1,380 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<profiles version="19">
|
||||||
|
<profile kind="CodeFormatterProfile" name="GoogleStyle" version="19">
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment" value="common_lines"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_logical_operator" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration" value="common_lines"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="common_lines"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration" value="common_lines"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.align_with_spaces" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_record_components" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="2"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_logical_operator" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line" value="one_line_never"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="100"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_method_body_on_one_line" value="one_line_if_empty"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line" value="one_line_never"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method" value="1"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line" value="one_line_if_empty"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line" value="one_line_never"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause" value="common_lines"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_additive_operator" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_relational_operator" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line" value="one_line_if_empty"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.wrap_before_shift_operator" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line" value="one_line_never"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_parameters" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_loops" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_relational_operator" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="3"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation" value="common_lines"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_additive_operator" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_declaration" value="end_of_line"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.text_block_indentation" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_module_statements" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line" value="one_line_never"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_additive_operator" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_shift_operator" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines" value="2147483647"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause" value="common_lines"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_code_block_on_one_line" value="one_line_never"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="2"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_not_operator" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration" value="common_lines"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line" value="one_line_never"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_arguments" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line" value="one_line_if_empty"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_logical_operator" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.wrap_before_relational_operator" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.indent_tag_description" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_constructor" value="end_of_line"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_string_concatenation" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.wrap_before_logical_operator" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_shift_operator" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration" value="common_lines"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_shift_operator" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line" value="one_line_never"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.wrap_before_additive_operator" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="false"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_relational_operator" value="insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.wrap_before_string_concatenation" value="true"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="0"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="100"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
|
||||||
|
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
|
@ -6,8 +6,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class FlingApplication {
|
public class FlingApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(FlingApplication.class, args);
|
SpringApplication.run(FlingApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,11 @@ package net.friedl.fling;
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.SerializationConfig;
|
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package net.friedl.fling.controller;
|
package net.friedl.fling.controller;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.core.io.InputStreamResource;
|
import org.springframework.core.io.InputStreamResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
@ -19,7 +17,6 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import net.friedl.fling.model.dto.ArtifactDto;
|
import net.friedl.fling.model.dto.ArtifactDto;
|
||||||
import net.friedl.fling.persistence.archive.ArchiveException;
|
import net.friedl.fling.persistence.archive.ArchiveException;
|
||||||
import net.friedl.fling.service.ArtifactService;
|
import net.friedl.fling.service.ArtifactService;
|
||||||
|
@ -28,55 +25,58 @@ import net.friedl.fling.service.ArtifactService;
|
||||||
@RequestMapping("/api")
|
@RequestMapping("/api")
|
||||||
public class ArtifactController {
|
public class ArtifactController {
|
||||||
|
|
||||||
private ArtifactService artifactService;
|
private ArtifactService artifactService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ArtifactController(ArtifactService artifactService) {
|
public ArtifactController(ArtifactService artifactService) {
|
||||||
this.artifactService = artifactService;
|
this.artifactService = artifactService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/artifacts", params = "flingId")
|
@GetMapping(path = "/artifacts", params = "flingId")
|
||||||
public List<ArtifactDto> getArtifacts(@RequestParam Long flingId) {
|
public List<ArtifactDto> getArtifacts(@RequestParam Long flingId) {
|
||||||
return artifactService.findAllArtifacts(flingId);
|
return artifactService.findAllArtifacts(flingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/artifacts", params = "artifactId")
|
@GetMapping(path = "/artifacts", params = "artifactId")
|
||||||
public ResponseEntity<ArtifactDto> getArtifact(@RequestParam Long artifactId) {
|
public ResponseEntity<ArtifactDto> getArtifact(@RequestParam Long artifactId) {
|
||||||
return ResponseEntity.of(artifactService.findArtifact(artifactId));
|
return ResponseEntity.of(artifactService.findArtifact(artifactId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/artifacts/{flingId}")
|
@PostMapping("/artifacts/{flingId}")
|
||||||
public ArtifactDto postArtifact(@PathVariable Long flingId, HttpServletRequest request) throws Exception {
|
public ArtifactDto postArtifact(@PathVariable Long flingId, HttpServletRequest request)
|
||||||
return artifactService.storeArtifact(flingId, request.getInputStream());
|
throws Exception {
|
||||||
}
|
return artifactService.storeArtifact(flingId, request.getInputStream());
|
||||||
|
}
|
||||||
|
|
||||||
@PatchMapping(path = "/artifacts/{artifactId}", consumes = MediaType.APPLICATION_JSON_VALUE)
|
@PatchMapping(path = "/artifacts/{artifactId}", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||||
public ArtifactDto patchArtifact(@PathVariable Long artifactId, @RequestBody String body) {
|
public ArtifactDto patchArtifact(@PathVariable Long artifactId, @RequestBody String body) {
|
||||||
return artifactService.mergeArtifact(artifactId, body);
|
return artifactService.mergeArtifact(artifactId, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping(path = "/artifacts/{artifactId}")
|
@DeleteMapping(path = "/artifacts/{artifactId}")
|
||||||
public void deleteArtifact(@PathVariable Long artifactId) throws ArchiveException {
|
public void deleteArtifact(@PathVariable Long artifactId) throws ArchiveException {
|
||||||
artifactService.deleteArtifact(artifactId);
|
artifactService.deleteArtifact(artifactId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/artifacts/{artifactId}/downloadid")
|
@GetMapping(path = "/artifacts/{artifactId}/downloadid")
|
||||||
public String getDownloadId(@PathVariable Long artifactId) {
|
public String getDownloadId(@PathVariable Long artifactId) {
|
||||||
return artifactService.generateDownloadId(artifactId);
|
return artifactService.generateDownloadId(artifactId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/artifacts/{artifactId}/{downloadId}/download")
|
@GetMapping(path = "/artifacts/{artifactId}/{downloadId}/download")
|
||||||
public ResponseEntity<Resource> downloadArtifact(@PathVariable Long artifactId, @PathVariable String downloadId)
|
public ResponseEntity<Resource> downloadArtifact(@PathVariable Long artifactId,
|
||||||
throws ArchiveException {
|
@PathVariable String downloadId)
|
||||||
|
throws ArchiveException {
|
||||||
|
|
||||||
var artifact = artifactService.findArtifact(artifactId).orElseThrow();
|
var artifact = artifactService.findArtifact(artifactId).orElseThrow();
|
||||||
var stream = new InputStreamResource(artifactService.downloadArtifact(downloadId));
|
var stream = new InputStreamResource(artifactService.downloadArtifact(downloadId));
|
||||||
|
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=\"" + artifact.getName() + "\"")
|
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||||
.contentLength(artifact.getSize())
|
"attachment;filename=\"" + artifact.getName() + "\"")
|
||||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
.contentLength(artifact.getSize())
|
||||||
.body(stream);
|
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
}
|
.body(stream);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package net.friedl.fling.controller;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.core.io.InputStreamResource;
|
import org.springframework.core.io.InputStreamResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
@ -18,7 +17,6 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import net.friedl.fling.model.dto.FlingDto;
|
import net.friedl.fling.model.dto.FlingDto;
|
||||||
import net.friedl.fling.persistence.archive.ArchiveException;
|
import net.friedl.fling.persistence.archive.ArchiveException;
|
||||||
import net.friedl.fling.service.FlingService;
|
import net.friedl.fling.service.FlingService;
|
||||||
|
@ -27,64 +25,66 @@ import net.friedl.fling.service.FlingService;
|
||||||
@RequestMapping("/api")
|
@RequestMapping("/api")
|
||||||
public class FlingController {
|
public class FlingController {
|
||||||
|
|
||||||
private FlingService flingService;
|
private FlingService flingService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public FlingController(FlingService flingService) {
|
public FlingController(FlingService flingService) {
|
||||||
this.flingService = flingService;
|
this.flingService = flingService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/fling")
|
@GetMapping("/fling")
|
||||||
public List<FlingDto> getFlings() {
|
public List<FlingDto> getFlings() {
|
||||||
return flingService.findAll();
|
return flingService.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/fling")
|
@PostMapping("/fling")
|
||||||
public Long postFling(@RequestBody FlingDto flingDto) {
|
public Long postFling(@RequestBody FlingDto flingDto) {
|
||||||
return flingService.createFling(flingDto);
|
return flingService.createFling(flingDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/fling/{flingId}")
|
@PutMapping("/fling/{flingId}")
|
||||||
public void putFling(@PathVariable Long flingId, @RequestBody FlingDto flingDto) {
|
public void putFling(@PathVariable Long flingId, @RequestBody FlingDto flingDto) {
|
||||||
flingService.mergeFling(flingId, flingDto);
|
flingService.mergeFling(flingId, flingDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/fling", params = "flingId")
|
@GetMapping(path = "/fling", params = "flingId")
|
||||||
public ResponseEntity<FlingDto> getFling(@RequestParam Long flingId) {
|
public ResponseEntity<FlingDto> getFling(@RequestParam Long flingId) {
|
||||||
return ResponseEntity.of(flingService.findFlingById(flingId));
|
return ResponseEntity.of(flingService.findFlingById(flingId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/fling", params = "shareId")
|
@GetMapping(path = "/fling", params = "shareId")
|
||||||
public ResponseEntity<FlingDto> getFlingByShareId(@RequestParam String shareId) {
|
public ResponseEntity<FlingDto> getFlingByShareId(@RequestParam String shareId) {
|
||||||
return ResponseEntity.of(flingService.findFlingByShareId(shareId));
|
return ResponseEntity.of(flingService.findFlingByShareId(shareId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/fling/shareExists/{shareId}")
|
@GetMapping(path = "/fling/shareExists/{shareId}")
|
||||||
public Boolean getShareExists(@PathVariable String shareId) {
|
public Boolean getShareExists(@PathVariable String shareId) {
|
||||||
return flingService.existsShareUrl(shareId);
|
return flingService.existsShareUrl(shareId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/fling/{flingId}")
|
@DeleteMapping("/fling/{flingId}")
|
||||||
public void deleteFling(@PathVariable Long flingId) {
|
public void deleteFling(@PathVariable Long flingId) {
|
||||||
flingService.deleteFlingById(flingId);
|
flingService.deleteFlingById(flingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/fling/{flingId}/package")
|
@GetMapping(path = "/fling/{flingId}/package")
|
||||||
public String packageFling(@PathVariable Long flingId) throws IOException, ArchiveException {
|
public String packageFling(@PathVariable Long flingId) throws IOException, ArchiveException {
|
||||||
return flingService.packageFling(flingId);
|
return flingService.packageFling(flingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/fling/{flingId}/download/{downloadId}")
|
@GetMapping(path = "/fling/{flingId}/download/{downloadId}")
|
||||||
public ResponseEntity<Resource> downloadFling(@PathVariable Long flingId, @PathVariable String downloadId) throws ArchiveException, IOException {
|
public ResponseEntity<Resource> downloadFling(@PathVariable Long flingId,
|
||||||
var fling = flingService.findFlingById(flingId).orElseThrow();
|
@PathVariable String downloadId) throws ArchiveException, IOException {
|
||||||
var flingPackage = flingService.downloadFling(downloadId);
|
var fling = flingService.findFlingById(flingId).orElseThrow();
|
||||||
var stream = new InputStreamResource(flingPackage.getFirst());
|
var flingPackage = flingService.downloadFling(downloadId);
|
||||||
|
var stream = new InputStreamResource(flingPackage.getFirst());
|
||||||
|
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=\"" + fling.getName() + ".zip" + "\"")
|
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||||
.contentLength(flingPackage.getSecond())
|
"attachment;filename=\"" + fling.getName() + ".zip" + "\"")
|
||||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
.contentLength(flingPackage.getSecond())
|
||||||
.body(stream);
|
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
}
|
.body(stream);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,23 @@
|
||||||
package net.friedl.fling.model.dto;
|
package net.friedl.fling.model.dto;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class ArtifactDto {
|
public class ArtifactDto {
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
private String path;
|
private String path;
|
||||||
|
|
||||||
private String doi;
|
private String doi;
|
||||||
|
|
||||||
private Long size;
|
private Long size;
|
||||||
|
|
||||||
private Integer version;
|
private Integer version;
|
||||||
|
|
||||||
private Instant uploadTime;
|
private Instant uploadTime;
|
||||||
|
|
||||||
private FlingDto fling;
|
private FlingDto fling;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,94 +3,94 @@ package net.friedl.fling.model.dto;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class FlingDto {
|
public class FlingDto {
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
private Instant creationTime;
|
private Instant creationTime;
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private Boolean directDownload;
|
private Boolean directDownload;
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private Boolean allowUpload;
|
private Boolean allowUpload;
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private Boolean shared;
|
private Boolean shared;
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private String shareUrl;
|
private String shareUrl;
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private Integer expirationClicks;
|
private Integer expirationClicks;
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private Instant expirationTime;
|
private Instant expirationTime;
|
||||||
|
|
||||||
private String authCode;
|
private String authCode;
|
||||||
|
|
||||||
@JsonProperty("sharing")
|
@JsonProperty("sharing")
|
||||||
private void unpackSharing(Map<String, Object> sharing) {
|
private void unpackSharing(Map<String, Object> sharing) {
|
||||||
this.directDownload = (Boolean) sharing.getOrDefault("directDownload", false);
|
this.directDownload = (Boolean) sharing.getOrDefault("directDownload", false);
|
||||||
this.allowUpload = (Boolean) sharing.getOrDefault("allowUpload", false);
|
this.allowUpload = (Boolean) sharing.getOrDefault("allowUpload", false);
|
||||||
this.shared = (Boolean) sharing.getOrDefault("shared", true);
|
this.shared = (Boolean) sharing.getOrDefault("shared", true);
|
||||||
this.shareUrl = (String) sharing.getOrDefault("shareUrl", null);
|
this.shareUrl = (String) sharing.getOrDefault("shareUrl", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("sharing")
|
||||||
|
private Map<String, Object> packSharing() {
|
||||||
|
Map<String, Object> sharing = new HashMap<>();
|
||||||
|
sharing.put("directDownload", this.directDownload);
|
||||||
|
sharing.put("allowUpload", this.allowUpload);
|
||||||
|
sharing.put("shared", this.shared);
|
||||||
|
sharing.put("shareUrl", this.shareUrl);
|
||||||
|
|
||||||
|
return sharing;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("expiration")
|
||||||
|
private void unpackExpiration(Map<String, Object> expiration) {
|
||||||
|
String type = (String) expiration.getOrDefault("type", null);
|
||||||
|
if (type == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "time":
|
||||||
|
this.expirationClicks = null;
|
||||||
|
// json can only handle int, long must be given as string
|
||||||
|
// TODO: this back and forth conversion is a bit hack-ish
|
||||||
|
this.expirationTime =
|
||||||
|
Instant.ofEpochMilli(Long.valueOf(expiration.get("value").toString()));
|
||||||
|
break;
|
||||||
|
case "clicks":
|
||||||
|
this.expirationTime = null;
|
||||||
|
this.expirationClicks = Integer.valueOf(expiration.get("value").toString());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unexpected value '" + type + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("expiration")
|
||||||
|
private Map<String, Object> packExpiration() {
|
||||||
|
Map<String, Object> expiration = new HashMap<>();
|
||||||
|
|
||||||
|
if (this.expirationClicks != null) {
|
||||||
|
expiration.put("type", "clicks");
|
||||||
|
expiration.put("value", this.expirationClicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonProperty("sharing")
|
if (this.expirationTime != null) {
|
||||||
private Map<String, Object> packSharing() {
|
expiration.put("type", "time");
|
||||||
Map<String, Object> sharing = new HashMap<>();
|
expiration.put("value", this.expirationTime.toEpochMilli());
|
||||||
sharing.put("directDownload", this.directDownload);
|
|
||||||
sharing.put("allowUpload", this.allowUpload);
|
|
||||||
sharing.put("shared", this.shared);
|
|
||||||
sharing.put("shareUrl", this.shareUrl);
|
|
||||||
|
|
||||||
return sharing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonProperty("expiration")
|
return expiration;
|
||||||
private void unpackExpiration(Map<String, Object> expiration) {
|
}
|
||||||
String type = (String) expiration.getOrDefault("type", null);
|
|
||||||
if(type == null) return;
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
case "time":
|
|
||||||
this.expirationClicks = null;
|
|
||||||
// json can only handle int, long must be given as string
|
|
||||||
// TODO: this back and forth conversion is a bit hack-ish
|
|
||||||
this.expirationTime = Instant.ofEpochMilli(Long.valueOf(expiration.get("value").toString()));
|
|
||||||
break;
|
|
||||||
case "clicks":
|
|
||||||
this.expirationTime = null;
|
|
||||||
this.expirationClicks = Integer.valueOf(expiration.get("value").toString());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unexpected value '"+type+"'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonProperty("expiration")
|
|
||||||
private Map<String, Object> packExpiration() {
|
|
||||||
Map<String, Object> expiration = new HashMap<>();
|
|
||||||
|
|
||||||
if(this.expirationClicks != null) {
|
|
||||||
expiration.put("type", "clicks");
|
|
||||||
expiration.put("value", this.expirationClicks);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.expirationTime != null) {
|
|
||||||
expiration.put("type", "time");
|
|
||||||
expiration.put("value", this.expirationTime.toEpochMilli());
|
|
||||||
}
|
|
||||||
|
|
||||||
return expiration;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@ import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class FlingSharingDto {
|
public class FlingSharingDto {
|
||||||
private Boolean allowUpload;
|
private Boolean allowUpload;
|
||||||
|
|
||||||
private Boolean directDownload;
|
private Boolean directDownload;
|
||||||
|
|
||||||
private String shareUrl;
|
private String shareUrl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,7 @@ import java.lang.reflect.Field;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.friedl.fling.model.dto.ArtifactDto;
|
import net.friedl.fling.model.dto.ArtifactDto;
|
||||||
import net.friedl.fling.persistence.entities.ArtifactEntity;
|
import net.friedl.fling.persistence.entities.ArtifactEntity;
|
||||||
|
@ -14,38 +12,38 @@ import net.friedl.fling.persistence.entities.ArtifactEntity;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Mapper(componentModel = "spring")
|
@Mapper(componentModel = "spring")
|
||||||
public abstract class ArtifactMapper {
|
public abstract class ArtifactMapper {
|
||||||
public abstract ArtifactDto map(ArtifactEntity artifactEntity);
|
public abstract ArtifactDto map(ArtifactEntity artifactEntity);
|
||||||
|
|
||||||
public abstract ArtifactEntity map(ArtifactDto artifactDto);
|
public abstract ArtifactEntity map(ArtifactDto artifactDto);
|
||||||
|
|
||||||
public abstract List<ArtifactDto> map(List<ArtifactEntity> artifactEntities);
|
public abstract List<ArtifactDto> map(List<ArtifactEntity> artifactEntities);
|
||||||
|
|
||||||
public Optional<ArtifactDto> map(Optional<ArtifactEntity> artifactEntity) {
|
public Optional<ArtifactDto> map(Optional<ArtifactEntity> artifactEntity) {
|
||||||
return artifactEntity.map(a -> map(a));
|
return artifactEntity.map(a -> map(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArtifactDto merge(ArtifactDto originalArtifactDto, Map<String, Object> patch) {
|
public ArtifactDto merge(ArtifactDto originalArtifactDto, Map<String, Object> patch) {
|
||||||
ArtifactDto mergedArtifactDto = new ArtifactDto();
|
ArtifactDto mergedArtifactDto = new ArtifactDto();
|
||||||
|
|
||||||
for (Field field : ArtifactDto.class.getDeclaredFields()) {
|
for (Field field : ArtifactDto.class.getDeclaredFields()) {
|
||||||
String fieldName = field.getName();
|
String fieldName = field.getName();
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
try {
|
try {
|
||||||
if (patch.containsKey(fieldName)) {
|
if (patch.containsKey(fieldName)) {
|
||||||
if(field.getType().equals(Long.class)) {
|
if (field.getType().equals(Long.class)) {
|
||||||
field.set(mergedArtifactDto, ((Number) patch.get(fieldName)).longValue());
|
field.set(mergedArtifactDto, ((Number) patch.get(fieldName)).longValue());
|
||||||
}
|
}
|
||||||
field.set(mergedArtifactDto, patch.get(fieldName));
|
field.set(mergedArtifactDto, patch.get(fieldName));
|
||||||
} else {
|
} else {
|
||||||
field.set(mergedArtifactDto, field.get(originalArtifactDto));
|
field.set(mergedArtifactDto, field.get(originalArtifactDto));
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IllegalArgumentException | IllegalAccessException e) {
|
|
||||||
log.error("Could not merge {} [value={}] with {}", fieldName, patch.get(fieldName), originalArtifactDto,
|
|
||||||
e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||||
return mergedArtifactDto;
|
log.error("Could not merge {} [value={}] with {}", fieldName, patch.get(fieldName),
|
||||||
|
originalArtifactDto,
|
||||||
|
e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return mergedArtifactDto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,21 +2,19 @@ package net.friedl.fling.model.mapper;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
|
|
||||||
import net.friedl.fling.model.dto.FlingDto;
|
import net.friedl.fling.model.dto.FlingDto;
|
||||||
import net.friedl.fling.persistence.entities.FlingEntity;
|
import net.friedl.fling.persistence.entities.FlingEntity;
|
||||||
|
|
||||||
@Mapper(componentModel = "spring")
|
@Mapper(componentModel = "spring")
|
||||||
public interface FlingMapper {
|
public interface FlingMapper {
|
||||||
FlingDto map(FlingEntity flingEntity);
|
FlingDto map(FlingEntity flingEntity);
|
||||||
|
|
||||||
default Optional<FlingDto> map(Optional<FlingEntity> flingEntity) {
|
default Optional<FlingDto> map(Optional<FlingEntity> flingEntity) {
|
||||||
return flingEntity.map(f -> map(f));
|
return flingEntity.map(f -> map(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
FlingEntity map(FlingDto flingDto);
|
FlingEntity map(FlingDto flingDto);
|
||||||
|
|
||||||
List<FlingDto> map(List<FlingEntity> flingEntities);
|
List<FlingDto> map(List<FlingEntity> flingEntities);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,37 +6,35 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
public interface Archive {
|
public interface Archive {
|
||||||
/**
|
/**
|
||||||
* Retrieve an artifact from the archive
|
* Retrieve an artifact from the archive
|
||||||
*
|
*
|
||||||
* @param id The unique artifact id as returned by {@link Archive#store}
|
* @param id The unique artifact id as returned by {@link Archive#store}
|
||||||
* @return An {@link InputStream} for reading the artifact
|
* @return An {@link InputStream} for reading the artifact
|
||||||
*/
|
*/
|
||||||
InputStream get(String id) throws ArchiveException;
|
InputStream get(String id) throws ArchiveException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store an artifact
|
* Store an artifact
|
||||||
*
|
*
|
||||||
* @param is The artifact represented as {@link InputStream}
|
* @param is The artifact represented as {@link InputStream}
|
||||||
* @return A unique archive id for the artifact
|
* @return A unique archive id for the artifact
|
||||||
* @throws IOException If anything goes wrong while storing the artifact in
|
* @throws IOException If anything goes wrong while storing the artifact in the archive
|
||||||
* the archive
|
*/
|
||||||
*/
|
String store(InputStream is) throws ArchiveException;
|
||||||
String store(InputStream is) throws ArchiveException;
|
|
||||||
|
|
||||||
default String store(File file) throws ArchiveException {
|
default String store(File file) throws ArchiveException {
|
||||||
try {
|
try {
|
||||||
return store(new FileInputStream(file));
|
return store(new FileInputStream(file));
|
||||||
}
|
} catch (IOException ex) {
|
||||||
catch (IOException ex) {
|
throw new ArchiveException(ex);
|
||||||
throw new ArchiveException(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete an artifact
|
* Delete an artifact
|
||||||
*
|
*
|
||||||
* @param id The unique artifact id as returned by {@link Archive#store}
|
* @param id The unique artifact id as returned by {@link Archive#store}
|
||||||
*/
|
*/
|
||||||
void remove(String id) throws ArchiveException;
|
void remove(String id) throws ArchiveException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,78 +1,73 @@
|
||||||
package net.friedl.fling.persistence.archive;
|
package net.friedl.fling.persistence.archive;
|
||||||
|
|
||||||
public class ArchiveException extends Exception {
|
public class ArchiveException extends Exception {
|
||||||
private static final long serialVersionUID = 6216735865308056261L;
|
private static final long serialVersionUID = 6216735865308056261L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new exception with {@code null} as its detail message. The
|
* Constructs a new exception with {@code null} as its detail message. The cause is not
|
||||||
* cause is not initialized, and may subsequently be initialized by a call
|
* initialized, and may subsequently be initialized by a call to {@link #initCause}.
|
||||||
* to {@link #initCause}.
|
*/
|
||||||
*/
|
public ArchiveException() {
|
||||||
public ArchiveException() {
|
super();
|
||||||
super();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new exception with the specified detail message. The cause
|
* Constructs a new exception with the specified detail message. The cause is not initialized, and
|
||||||
* is not initialized, and may subsequently be initialized by a call to
|
* may subsequently be initialized by a call to {@link #initCause}.
|
||||||
* {@link #initCause}.
|
*
|
||||||
*
|
* @param message the detail message. The detail message is saved for later retrieval by the
|
||||||
* @param message the detail message. The detail message is saved for later
|
* {@link #getMessage()} method.
|
||||||
* retrieval by the {@link #getMessage()} method.
|
*/
|
||||||
*/
|
public ArchiveException(String message) {
|
||||||
public ArchiveException(String message) {
|
super(message);
|
||||||
super(message);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new exception with the specified detail message and cause.
|
* Constructs a new exception with the specified detail message and cause.
|
||||||
* <p>
|
* <p>
|
||||||
* Note that the detail message associated with {@code cause} is <i>not</i>
|
* Note that the detail message associated with {@code cause} is <i>not</i> automatically
|
||||||
* automatically incorporated in this exception's detail message.
|
* incorporated in this exception's detail message.
|
||||||
*
|
*
|
||||||
* @param message the detail message (which is saved for later retrieval by
|
* @param message the detail message (which is saved for later retrieval by the
|
||||||
* the {@link #getMessage()} method).
|
* {@link #getMessage()} method).
|
||||||
* @param cause the cause (which is saved for later retrieval by the
|
* @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method).
|
||||||
* {@link #getCause()} method). (A {@code null} value is permitted,
|
* (A {@code null} value is permitted, and indicates that the cause is nonexistent or
|
||||||
* and indicates that the cause is nonexistent or unknown.)
|
* unknown.)
|
||||||
* @since 1.4
|
* @since 1.4
|
||||||
*/
|
*/
|
||||||
public ArchiveException(String message, Throwable cause) {
|
public ArchiveException(String message, Throwable cause) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new exception with the specified cause and a detail message
|
* Constructs a new exception with the specified cause and a detail message of
|
||||||
* of {@code (cause==null ? null : cause.toString())} (which typically
|
* {@code (cause==null ? null : cause.toString())} (which typically contains the class and detail
|
||||||
* contains the class and detail message of {@code cause}). This constructor
|
* message of {@code cause}). This constructor is useful for exceptions that are little more than
|
||||||
* is useful for exceptions that are little more than wrappers for other
|
* wrappers for other throwables (for example,
|
||||||
* throwables (for example,
|
* {@link java.security.PrivilegedActionArchiveException}).
|
||||||
* {@link java.security.PrivilegedActionArchiveException}).
|
*
|
||||||
*
|
* @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method).
|
||||||
* @param cause the cause (which is saved for later retrieval by the
|
* (A {@code null} value is permitted, and indicates that the cause is nonexistent or
|
||||||
* {@link #getCause()} method). (A {@code null} value is permitted,
|
* unknown.)
|
||||||
* and indicates that the cause is nonexistent or unknown.)
|
* @since 1.4
|
||||||
* @since 1.4
|
*/
|
||||||
*/
|
public ArchiveException(Throwable cause) {
|
||||||
public ArchiveException(Throwable cause) {
|
super(cause);
|
||||||
super(cause);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new exception with the specified detail message, cause,
|
* Constructs a new exception with the specified detail message, cause, suppression enabled or
|
||||||
* suppression enabled or disabled, and writable stack trace enabled or
|
* disabled, and writable stack trace enabled or disabled.
|
||||||
* disabled.
|
*
|
||||||
*
|
* @param message the detail message.
|
||||||
* @param message the detail message.
|
* @param cause the cause. (A {@code null} value is permitted, and indicates that the cause is
|
||||||
* @param cause the cause. (A {@code null} value is permitted, and indicates
|
* nonexistent or unknown.)
|
||||||
* that the cause is nonexistent or unknown.)
|
* @param enableSuppression whether or not suppression is enabled or disabled
|
||||||
* @param enableSuppression whether or not suppression is enabled or
|
* @param writableStackTrace whether or not the stack trace should be writable
|
||||||
* disabled
|
* @since 1.7
|
||||||
* @param writableStackTrace whether or not the stack trace should be
|
*/
|
||||||
* writable
|
protected ArchiveException(String message, Throwable cause, boolean enableSuppression,
|
||||||
* @since 1.7
|
boolean writableStackTrace) {
|
||||||
*/
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
protected ArchiveException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
}
|
||||||
super(message, cause, enableSuppression, writableStackTrace);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,71 +10,71 @@ import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import net.friedl.fling.persistence.archive.Archive;
|
import net.friedl.fling.persistence.archive.Archive;
|
||||||
import net.friedl.fling.persistence.archive.ArchiveException;
|
import net.friedl.fling.persistence.archive.ArchiveException;
|
||||||
|
|
||||||
@Component("fileSystemArchive")
|
@Component("fileSystemArchive")
|
||||||
public class FileSystemArchive implements Archive {
|
public class FileSystemArchive implements Archive {
|
||||||
private MessageDigest fileStoreDigest;
|
private MessageDigest fileStoreDigest;
|
||||||
|
|
||||||
private FileSystemArchiveConfiguration configuration;
|
private FileSystemArchiveConfiguration configuration;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public FileSystemArchive(MessageDigest fileStoreDigest, FileSystemArchiveConfiguration configuration) {
|
public FileSystemArchive(MessageDigest fileStoreDigest,
|
||||||
this.fileStoreDigest = fileStoreDigest;
|
FileSystemArchiveConfiguration configuration) {
|
||||||
this.configuration = configuration;
|
this.fileStoreDigest = fileStoreDigest;
|
||||||
|
this.configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream get(String id) throws ArchiveException {
|
||||||
|
try {
|
||||||
|
var path = Paths.get(configuration.getDirectory(), id);
|
||||||
|
FileInputStream fis = new FileInputStream(path.toFile());
|
||||||
|
return fis;
|
||||||
|
} catch (FileNotFoundException ex) {
|
||||||
|
throw new ArchiveException(ex);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream get(String id) throws ArchiveException {
|
public String store(InputStream is) throws ArchiveException {
|
||||||
try {
|
try {
|
||||||
var path = Paths.get(configuration.getDirectory(), id);
|
byte[] fileBytes = is.readAllBytes();
|
||||||
FileInputStream fis = new FileInputStream(path.toFile());
|
is.close();
|
||||||
return fis;
|
|
||||||
} catch (FileNotFoundException ex) {
|
String fileStoreId = hexEncode(fileStoreDigest.digest(fileBytes));
|
||||||
throw new ArchiveException(ex);
|
|
||||||
}
|
FileChannel fc = FileChannel.open(Paths.get(configuration.getDirectory(), fileStoreId),
|
||||||
|
StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE,
|
||||||
|
StandardOpenOption.CREATE);
|
||||||
|
|
||||||
|
fc.write(ByteBuffer.wrap(fileBytes));
|
||||||
|
|
||||||
|
fc.close();
|
||||||
|
return fileStoreId;
|
||||||
|
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new ArchiveException(ex);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String store(InputStream is) throws ArchiveException {
|
public void remove(String id) throws ArchiveException {
|
||||||
try {
|
var path = Paths.get(configuration.getDirectory(), id);
|
||||||
byte[] fileBytes = is.readAllBytes();
|
try {
|
||||||
is.close();
|
Files.deleteIfExists(path);
|
||||||
|
} catch (IOException e) {
|
||||||
String fileStoreId = hexEncode(fileStoreDigest.digest(fileBytes));
|
throw new ArchiveException("Could not delete file at " + path.toString(), e);
|
||||||
|
|
||||||
FileChannel fc = FileChannel.open(Paths.get(configuration.getDirectory(), fileStoreId),
|
|
||||||
StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
|
|
||||||
|
|
||||||
fc.write(ByteBuffer.wrap(fileBytes));
|
|
||||||
|
|
||||||
fc.close();
|
|
||||||
return fileStoreId;
|
|
||||||
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new ArchiveException(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
private String hexEncode(byte[] fileStoreId) {
|
||||||
public void remove(String id) throws ArchiveException {
|
StringBuilder sb = new StringBuilder(fileStoreId.length * 2);
|
||||||
var path = Paths.get(configuration.getDirectory(), id);
|
for (byte b : fileStoreId)
|
||||||
try {
|
sb.append(String.format("%02x", b));
|
||||||
Files.deleteIfExists(path);
|
return sb.toString();
|
||||||
} catch (IOException e) {
|
}
|
||||||
throw new ArchiveException("Could not delete file at " + path.toString(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String hexEncode(byte[] fileStoreId) {
|
|
||||||
StringBuilder sb = new StringBuilder(fileStoreId.length * 2);
|
|
||||||
for (byte b : fileStoreId)
|
|
||||||
sb.append(String.format("%02x", b));
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,14 +5,11 @@ import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
@ -21,23 +18,24 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConfigurationProperties("fling.archive.fileystem")
|
@ConfigurationProperties("fling.archive.fileystem")
|
||||||
@ConditionalOnBean(FileSystemArchive.class)
|
@ConditionalOnBean(FileSystemArchive.class)
|
||||||
@Getter @Setter
|
@Getter
|
||||||
|
@Setter
|
||||||
public class FileSystemArchiveConfiguration {
|
public class FileSystemArchiveConfiguration {
|
||||||
private String directory;
|
private String directory;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public MessageDigest fileStoreDigest() throws NoSuchAlgorithmException {
|
public MessageDigest fileStoreDigest() throws NoSuchAlgorithmException {
|
||||||
return MessageDigest.getInstance("SHA-512");
|
return MessageDigest.getInstance("SHA-512");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() throws IOException {
|
||||||
|
if (directory == null) {
|
||||||
|
log.info("Directory not configured take temp path");
|
||||||
|
Path tmpPath = Files.createTempDirectory("fling");
|
||||||
|
this.directory = tmpPath.toAbsolutePath().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
log.info("File store directory: {}", directory);
|
||||||
public void init() throws IOException {
|
}
|
||||||
if (directory == null) {
|
|
||||||
log.info("Directory not configured take temp path");
|
|
||||||
Path tmpPath = Files.createTempDirectory("fling");
|
|
||||||
this.directory = tmpPath.toAbsolutePath().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("File store directory: {}", directory);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package net.friedl.fling.persistence.entities;
|
package net.friedl.fling.persistence.entities;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
|
@ -9,38 +8,39 @@ import javax.persistence.Id;
|
||||||
import javax.persistence.ManyToOne;
|
import javax.persistence.ManyToOne;
|
||||||
import javax.persistence.PrePersist;
|
import javax.persistence.PrePersist;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "Artifact")
|
@Table(name = "Artifact")
|
||||||
@Getter @Setter
|
@Getter
|
||||||
|
@Setter
|
||||||
public class ArtifactEntity {
|
public class ArtifactEntity {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue
|
@GeneratedValue
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private Integer version;
|
private Integer version;
|
||||||
|
|
||||||
private String path;
|
private String path;
|
||||||
|
|
||||||
@Column(unique = true)
|
@Column(unique = true)
|
||||||
private String doi;
|
private String doi;
|
||||||
|
|
||||||
private Instant uploadTime;
|
private Instant uploadTime;
|
||||||
|
|
||||||
private Long size;
|
private Long size;
|
||||||
|
|
||||||
@ManyToOne(optional = false)
|
@ManyToOne(optional = false)
|
||||||
private FlingEntity fling;
|
private FlingEntity fling;
|
||||||
|
|
||||||
@PrePersist
|
@PrePersist
|
||||||
private void prePersist() {
|
private void prePersist() {
|
||||||
this.uploadTime = Instant.now();
|
this.uploadTime = Instant.now();
|
||||||
|
|
||||||
if(this.version == null) this.version = -1;
|
if (this.version == null)
|
||||||
}
|
this.version = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package net.friedl.fling.persistence.entities;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.persistence.CascadeType;
|
import javax.persistence.CascadeType;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
@ -12,7 +11,6 @@ import javax.persistence.OneToMany;
|
||||||
import javax.persistence.PostPersist;
|
import javax.persistence.PostPersist;
|
||||||
import javax.persistence.PrePersist;
|
import javax.persistence.PrePersist;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@ -21,52 +19,52 @@ import lombok.Setter;
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public class FlingEntity {
|
public class FlingEntity {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue
|
@GeneratedValue
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private Instant creationTime;
|
private Instant creationTime;
|
||||||
|
|
||||||
private Instant expirationTime;
|
private Instant expirationTime;
|
||||||
|
|
||||||
private Integer expirationClicks;
|
private Integer expirationClicks;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private Boolean directDownload;
|
private Boolean directDownload;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private Boolean allowUpload;
|
private Boolean allowUpload;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private Boolean shared;
|
private Boolean shared;
|
||||||
|
|
||||||
@Column(unique = true, nullable = false)
|
@Column(unique = true, nullable = false)
|
||||||
private String shareUrl;
|
private String shareUrl;
|
||||||
|
|
||||||
private String authCode;
|
private String authCode;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "fling", cascade = CascadeType.ALL, orphanRemoval = true)
|
@OneToMany(mappedBy = "fling", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||||
private Set<ArtifactEntity> artifacts;
|
private Set<ArtifactEntity> artifacts;
|
||||||
|
|
||||||
@PrePersist
|
@PrePersist
|
||||||
private void prePersist() {
|
private void prePersist() {
|
||||||
if (this.directDownload == null)
|
if (this.directDownload == null)
|
||||||
this.directDownload = false;
|
this.directDownload = false;
|
||||||
if (this.allowUpload == null)
|
if (this.allowUpload == null)
|
||||||
this.allowUpload = false;
|
this.allowUpload = false;
|
||||||
if (this.shared == null)
|
if (this.shared == null)
|
||||||
this.shared = true;
|
this.shared = true;
|
||||||
|
|
||||||
this.creationTime = Instant.now();
|
this.creationTime = Instant.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostPersist
|
@PostPersist
|
||||||
private void postPersist() {
|
private void postPersist() {
|
||||||
System.out.println("ID: "+this.id);
|
System.out.println("ID: " + this.id);
|
||||||
System.out.println("Share Url: "+this.shareUrl);
|
System.out.println("Share Url: " + this.shareUrl);
|
||||||
|
|
||||||
this.shareUrl = this.id+this.shareUrl;
|
this.shareUrl = this.id + this.shareUrl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,13 @@ package net.friedl.fling.persistence.repositories;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
import net.friedl.fling.persistence.entities.ArtifactEntity;
|
import net.friedl.fling.persistence.entities.ArtifactEntity;
|
||||||
|
|
||||||
public interface ArtifactRepository extends JpaRepository<ArtifactEntity, Long> {
|
public interface ArtifactRepository extends JpaRepository<ArtifactEntity, Long> {
|
||||||
Optional<ArtifactEntity> findByDoi(String doi);
|
Optional<ArtifactEntity> findByDoi(String doi);
|
||||||
List<ArtifactEntity> deleteByDoi(String doi);
|
|
||||||
List<ArtifactEntity> findAllByFlingId(Long flingId);
|
List<ArtifactEntity> deleteByDoi(String doi);
|
||||||
|
|
||||||
|
List<ArtifactEntity> findAllByFlingId(Long flingId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
package net.friedl.fling.persistence.repositories;
|
package net.friedl.fling.persistence.repositories;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
|
||||||
import net.friedl.fling.persistence.entities.FlingEntity;
|
import net.friedl.fling.persistence.entities.FlingEntity;
|
||||||
|
|
||||||
public interface FlingRepository extends JpaRepository<FlingEntity, Long> {
|
public interface FlingRepository extends JpaRepository<FlingEntity, Long> {
|
||||||
Optional<FlingEntity> findByName(String name);
|
Optional<FlingEntity> findByName(String name);
|
||||||
|
|
||||||
Optional<FlingEntity> findByShareUrl(String shareUrl);
|
Optional<FlingEntity> findByShareUrl(String shareUrl);
|
||||||
|
|
||||||
@Query("SELECT COUNT(*) FROM ArtifactEntity a, FlingEntity f where a.fling=f.id and f.id=:flingId")
|
@Query("SELECT COUNT(*) FROM ArtifactEntity a, FlingEntity f where a.fling=f.id and f.id=:flingId")
|
||||||
Long countArtifactsById(Long flingId);
|
Long countArtifactsById(Long flingId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
package net.friedl.fling.security;
|
package net.friedl.fling.security;
|
||||||
|
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.friedl.fling.security.authentication.FlingToken;
|
import net.friedl.fling.security.authentication.FlingToken;
|
||||||
import net.friedl.fling.security.authentication.dto.UserAuthDto;
|
import net.friedl.fling.security.authentication.dto.UserAuthDto;
|
||||||
|
@ -17,70 +14,76 @@ import net.friedl.fling.service.FlingService;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class AuthorizationService {
|
public class AuthorizationService {
|
||||||
private FlingService flingService;
|
private FlingService flingService;
|
||||||
private ArtifactService artifactService;
|
private ArtifactService artifactService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public AuthorizationService(FlingService flingService, ArtifactService artifactService) {
|
public AuthorizationService(FlingService flingService, ArtifactService artifactService) {
|
||||||
this.flingService = flingService;
|
this.flingService = flingService;
|
||||||
this.artifactService = artifactService;
|
this.artifactService = artifactService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean allowUpload(Long flingId, AbstractAuthenticationToken token) {
|
||||||
|
if (!(token instanceof FlingToken))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
FlingToken flingToken = (FlingToken) token;
|
||||||
|
if (flingToken.getGrantedFlingAuthority().getAuthority()
|
||||||
|
.equals(FlingAuthority.FLING_OWNER.name())) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allowUpload(Long flingId, AbstractAuthenticationToken token) {
|
var uploadAllowed = flingService.findFlingById(flingId).orElseThrow().getAllowUpload();
|
||||||
if (!(token instanceof FlingToken)) return false;
|
|
||||||
|
|
||||||
FlingToken flingToken = (FlingToken) token;
|
return uploadAllowed && flingToken.getGrantedFlingAuthority().getFlingId().equals(flingId);
|
||||||
if (flingToken.getGrantedFlingAuthority().getAuthority().equals(FlingAuthority.FLING_OWNER.name())) {
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var uploadAllowed = flingService.findFlingById(flingId).orElseThrow().getAllowUpload();
|
public boolean allowPatchingArtifact(Long artifactId, FlingToken authentication) {
|
||||||
|
var flingId = artifactService.findArtifact(artifactId).orElseThrow().getFling().getId();
|
||||||
|
return allowUpload(flingId, authentication);
|
||||||
|
}
|
||||||
|
|
||||||
return uploadAllowed && flingToken.getGrantedFlingAuthority().getFlingId().equals(flingId);
|
public boolean allowFlingAccess(UserAuthDto userAuth, String shareUrl) {
|
||||||
|
return userAuth.getShareId().equals(shareUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean allowFlingAccess(Long flingId, AbstractAuthenticationToken token) {
|
||||||
|
if (!(token instanceof FlingToken))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
FlingToken flingToken = (FlingToken) token;
|
||||||
|
if (flingToken.getGrantedFlingAuthority().getAuthority()
|
||||||
|
.equals(FlingAuthority.FLING_OWNER.name())) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allowPatchingArtifact(Long artifactId, FlingToken authentication) {
|
return flingToken.getGrantedFlingAuthority().getFlingId().equals(flingId);
|
||||||
var flingId = artifactService.findArtifact(artifactId).orElseThrow().getFling().getId();
|
}
|
||||||
return allowUpload(flingId, authentication);
|
|
||||||
|
public boolean allowFlingAccess(AbstractAuthenticationToken token, HttpServletRequest request) {
|
||||||
|
if (!(token instanceof FlingToken))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
FlingToken flingToken = (FlingToken) token;
|
||||||
|
if (flingToken.getGrantedFlingAuthority().getAuthority()
|
||||||
|
.equals(FlingAuthority.FLING_OWNER.name())) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allowFlingAccess(UserAuthDto userAuth, String shareUrl) {
|
var shareId = request.getParameter("shareId");
|
||||||
return userAuth.getShareId().equals(shareUrl);
|
|
||||||
|
Long flingId;
|
||||||
|
|
||||||
|
try {
|
||||||
|
flingId = shareId != null
|
||||||
|
? flingService.findFlingByShareId(shareId).orElseThrow().getId()
|
||||||
|
: Long.parseLong(request.getParameter("flingId"));
|
||||||
|
} catch (NumberFormatException | NoSuchElementException e) {
|
||||||
|
log.warn("Invalid shareId [shareId=\"{}\"] or flingId [flingId=\"{}\"] found",
|
||||||
|
request.getParameter("shareId"), request.getParameter("flingId"));
|
||||||
|
flingId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allowFlingAccess(Long flingId, AbstractAuthenticationToken token) {
|
return flingToken.getGrantedFlingAuthority().getFlingId().equals(flingId);
|
||||||
if (!(token instanceof FlingToken)) return false;
|
}
|
||||||
|
|
||||||
FlingToken flingToken = (FlingToken) token;
|
|
||||||
if (flingToken.getGrantedFlingAuthority().getAuthority().equals(FlingAuthority.FLING_OWNER.name())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return flingToken.getGrantedFlingAuthority().getFlingId().equals(flingId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean allowFlingAccess(AbstractAuthenticationToken token, HttpServletRequest request) {
|
|
||||||
if (!(token instanceof FlingToken)) return false;
|
|
||||||
|
|
||||||
FlingToken flingToken = (FlingToken) token;
|
|
||||||
if (flingToken.getGrantedFlingAuthority().getAuthority().equals(FlingAuthority.FLING_OWNER.name())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var shareId = request.getParameter("shareId");
|
|
||||||
|
|
||||||
Long flingId;
|
|
||||||
|
|
||||||
try {
|
|
||||||
flingId = shareId != null
|
|
||||||
? flingService.findFlingByShareId(shareId).orElseThrow().getId()
|
|
||||||
: Long.parseLong(request.getParameter("flingId"));
|
|
||||||
} catch (NumberFormatException | NoSuchElementException e) {
|
|
||||||
log.warn("Invalid shareId [shareId=\"{}\"] or flingId [flingId=\"{}\"] found",
|
|
||||||
request.getParameter("shareId"), request.getParameter("flingId"));
|
|
||||||
flingId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return flingToken.getGrantedFlingAuthority().getFlingId().equals(flingId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package net.friedl.fling.security;
|
package net.friedl.fling.security;
|
||||||
|
|
||||||
public enum FlingAuthority {
|
public enum FlingAuthority {
|
||||||
FLING_OWNER,
|
FLING_OWNER, FLING_USER
|
||||||
FLING_USER
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,9 @@ package net.friedl.fling.security;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
import io.jsonwebtoken.JwtParser;
|
import io.jsonwebtoken.JwtParser;
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
import io.jsonwebtoken.security.Keys;
|
import io.jsonwebtoken.security.Keys;
|
||||||
|
@ -17,26 +15,26 @@ import lombok.Data;
|
||||||
@Data
|
@Data
|
||||||
@ConfigurationProperties("fling.security")
|
@ConfigurationProperties("fling.security")
|
||||||
public class FlingSecurityConfiguration {
|
public class FlingSecurityConfiguration {
|
||||||
private List<String> allowedOrigins;
|
private List<String> allowedOrigins;
|
||||||
|
|
||||||
private String adminUser;
|
private String adminUser;
|
||||||
|
|
||||||
private String adminPassword;
|
private String adminPassword;
|
||||||
|
|
||||||
private String signingKey;
|
private String signingKey;
|
||||||
|
|
||||||
private Long jwtExpiration;
|
private Long jwtExpiration;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Key jwtSigningKey() {
|
public Key jwtSigningKey() {
|
||||||
byte[] key = signingKey.getBytes(StandardCharsets.UTF_8);
|
byte[] key = signingKey.getBytes(StandardCharsets.UTF_8);
|
||||||
return Keys.hmacShaKeyFor(key);
|
return Keys.hmacShaKeyFor(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public JwtParser jwtParser(Key jwtSignigKey) {
|
public JwtParser jwtParser(Key jwtSignigKey) {
|
||||||
return Jwts.parserBuilder()
|
return Jwts.parserBuilder()
|
||||||
.setSigningKey(jwtSignigKey)
|
.setSigningKey(jwtSignigKey)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
package net.friedl.fling.security;
|
package net.friedl.fling.security;
|
||||||
|
|
||||||
import static org.springframework.security.config.Customizer.withDefaults;
|
import static org.springframework.security.config.Customizer.withDefaults;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
@ -20,7 +18,6 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
import org.springframework.web.cors.CorsConfigurationSource;
|
import org.springframework.web.cors.CorsConfigurationSource;
|
||||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
@ -32,23 +29,23 @@ import net.friedl.fling.security.authentication.JwtAuthenticationFilter;
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public class FlingWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
|
public class FlingWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
|
||||||
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||||
private AuthorizationService authorizationService;
|
private AuthorizationService authorizationService;
|
||||||
private FlingSecurityConfiguration securityConfiguration;
|
private FlingSecurityConfiguration securityConfiguration;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public FlingWebSecurityConfigurer(JwtAuthenticationFilter jwtAuthenticationFilter,
|
public FlingWebSecurityConfigurer(JwtAuthenticationFilter jwtAuthenticationFilter,
|
||||||
AuthorizationService authorizationService,
|
AuthorizationService authorizationService,
|
||||||
FlingSecurityConfiguration securityConfiguraiton) {
|
FlingSecurityConfiguration securityConfiguraiton) {
|
||||||
|
|
||||||
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
|
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
|
||||||
this.authorizationService = authorizationService;
|
this.authorizationService = authorizationService;
|
||||||
this.securityConfiguration = securityConfiguraiton;
|
this.securityConfiguration = securityConfiguraiton;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
http
|
http
|
||||||
.csrf().disable()
|
.csrf().disable()
|
||||||
.cors(withDefaults())
|
.cors(withDefaults())
|
||||||
|
@ -102,45 +99,46 @@ public class FlingWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
|
||||||
.hasAuthority(FlingAuthority.FLING_OWNER.name());
|
.hasAuthority(FlingAuthority.FLING_OWNER.name());
|
||||||
|
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private RequestMatcher modificationMethodsAntMatcher(String antPattern) {
|
private RequestMatcher modificationMethodsAntMatcher(String antPattern) {
|
||||||
return multiMethodAntMatcher(antPattern,
|
return multiMethodAntMatcher(antPattern,
|
||||||
HttpMethod.PATCH, HttpMethod.PUT,
|
HttpMethod.PATCH, HttpMethod.PUT,
|
||||||
HttpMethod.POST, HttpMethod.DELETE);
|
HttpMethod.POST, HttpMethod.DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private RequestMatcher multiMethodAntMatcher(String antPattern, HttpMethod... httpMethods) {
|
private RequestMatcher multiMethodAntMatcher(String antPattern, HttpMethod... httpMethods) {
|
||||||
List<RequestMatcher> antMatchers = Arrays.stream(httpMethods)
|
List<RequestMatcher> antMatchers = Arrays.stream(httpMethods)
|
||||||
.map(m -> new AntPathRequestMatcher(antPattern, m.toString()))
|
.map(m -> new AntPathRequestMatcher(antPattern, m.toString()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
return new OrRequestMatcher(antMatchers);
|
return new OrRequestMatcher(antMatchers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public CorsConfigurationSource corsConfigurationSource() {
|
public CorsConfigurationSource corsConfigurationSource() {
|
||||||
// see https://stackoverflow.com/a/43559266
|
// see https://stackoverflow.com/a/43559266
|
||||||
|
|
||||||
log.info("Allowed origins: {}", securityConfiguration.getAllowedOrigins());
|
log.info("Allowed origins: {}", securityConfiguration.getAllowedOrigins());
|
||||||
|
|
||||||
CorsConfiguration configuration = new CorsConfiguration();
|
CorsConfiguration configuration = new CorsConfiguration();
|
||||||
configuration.setAllowedOrigins(securityConfiguration.getAllowedOrigins());
|
configuration.setAllowedOrigins(securityConfiguration.getAllowedOrigins());
|
||||||
configuration.setAllowedMethods(List.of("*"));
|
configuration.setAllowedMethods(List.of("*"));
|
||||||
|
|
||||||
// setAllowCredentials(true) is important, otherwise:
|
// setAllowCredentials(true) is important, otherwise:
|
||||||
// The value of the 'Access-Control-Allow-Origin' header in the response
|
// The value of the 'Access-Control-Allow-Origin' header in the response
|
||||||
// must not be the wildcard '*' when the request's credentials mode is
|
// must not be the wildcard '*' when the request's credentials mode is
|
||||||
// 'include'.
|
// 'include'.
|
||||||
configuration.setAllowCredentials(true);
|
configuration.setAllowCredentials(true);
|
||||||
|
|
||||||
// setAllowedHeaders is important! Without it, OPTIONS preflight request
|
// setAllowedHeaders is important! Without it, OPTIONS preflight request
|
||||||
// will fail with 403 Invalid CORS request
|
// will fail with 403 Invalid CORS request
|
||||||
configuration.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type", "Origin"));
|
configuration
|
||||||
|
.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type", "Origin"));
|
||||||
|
|
||||||
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||||
source.registerCorsConfiguration("/**", configuration);
|
source.registerCorsConfiguration("/**", configuration);
|
||||||
|
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import net.friedl.fling.security.authentication.dto.OwnerAuthDto;
|
import net.friedl.fling.security.authentication.dto.OwnerAuthDto;
|
||||||
import net.friedl.fling.security.authentication.dto.UserAuthDto;
|
import net.friedl.fling.security.authentication.dto.UserAuthDto;
|
||||||
|
|
||||||
|
@ -13,20 +12,20 @@ import net.friedl.fling.security.authentication.dto.UserAuthDto;
|
||||||
@RequestMapping("/api")
|
@RequestMapping("/api")
|
||||||
public class AuthenticationController {
|
public class AuthenticationController {
|
||||||
|
|
||||||
private AuthenticationService authenticationService;
|
private AuthenticationService authenticationService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public AuthenticationController(AuthenticationService authenticationService) {
|
public AuthenticationController(AuthenticationService authenticationService) {
|
||||||
this.authenticationService = authenticationService;
|
this.authenticationService = authenticationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/auth/owner")
|
@PostMapping("/auth/owner")
|
||||||
public String authenticateOwner(@RequestBody OwnerAuthDto ownerAuthDto) {
|
public String authenticateOwner(@RequestBody OwnerAuthDto ownerAuthDto) {
|
||||||
return authenticationService.authenticate(ownerAuthDto);
|
return authenticationService.authenticate(ownerAuthDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/auth/user")
|
@PostMapping("/auth/user")
|
||||||
public String authenticateUser(@RequestBody UserAuthDto userAuthDto) {
|
public String authenticateUser(@RequestBody UserAuthDto userAuthDto) {
|
||||||
return authenticationService.authenticate(userAuthDto);
|
return authenticationService.authenticate(userAuthDto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,11 @@ package net.friedl.fling.security.authentication;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import io.jsonwebtoken.JwtBuilder;
|
import io.jsonwebtoken.JwtBuilder;
|
||||||
import io.jsonwebtoken.JwtParser;
|
import io.jsonwebtoken.JwtParser;
|
||||||
|
@ -22,81 +20,81 @@ import net.friedl.fling.service.FlingService;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class AuthenticationService {
|
public class AuthenticationService {
|
||||||
private FlingService flingService;
|
private FlingService flingService;
|
||||||
private JwtParser jwtParser;
|
private JwtParser jwtParser;
|
||||||
private Key signingKey;
|
private Key signingKey;
|
||||||
private FlingSecurityConfiguration securityConfig;
|
private FlingSecurityConfiguration securityConfig;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public AuthenticationService(JwtParser jwtParser, Key signingKey, FlingService flingService,
|
public AuthenticationService(JwtParser jwtParser, Key signingKey, FlingService flingService,
|
||||||
FlingSecurityConfiguration securityConfig) {
|
FlingSecurityConfiguration securityConfig) {
|
||||||
this.flingService = flingService;
|
this.flingService = flingService;
|
||||||
this.jwtParser = jwtParser;
|
this.jwtParser = jwtParser;
|
||||||
this.signingKey = signingKey;
|
this.signingKey = signingKey;
|
||||||
this.securityConfig = securityConfig;
|
this.securityConfig = securityConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String authenticate(OwnerAuthDto ownerAuth) {
|
||||||
|
if (!securityConfig.getAdminUser().equals(ownerAuth.getUsername())) {
|
||||||
|
throw new AccessDeniedException("Wrong credentials");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String authenticate(OwnerAuthDto ownerAuth) {
|
if (!securityConfig.getAdminPassword().equals(ownerAuth.getPassword())) {
|
||||||
if (!securityConfig.getAdminUser().equals(ownerAuth.getUsername())) {
|
throw new AccessDeniedException("Wrong credentials");
|
||||||
throw new AccessDeniedException("Wrong credentials");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!securityConfig.getAdminPassword().equals(ownerAuth.getPassword())) {
|
|
||||||
throw new AccessDeniedException("Wrong credentials");
|
|
||||||
}
|
|
||||||
|
|
||||||
return makeBaseBuilder()
|
|
||||||
.setSubject("owner")
|
|
||||||
.compact();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String authenticate(UserAuthDto userAuth) {
|
return makeBaseBuilder()
|
||||||
var fling = flingService.findFlingByShareId(userAuth.getShareId())
|
.setSubject("owner")
|
||||||
.orElseThrow();
|
.compact();
|
||||||
String authCode = userAuth.getCode();
|
}
|
||||||
|
|
||||||
if (!flingService.hasAuthCode(fling.getId(), authCode)) {
|
public String authenticate(UserAuthDto userAuth) {
|
||||||
throw new AccessDeniedException("Wrong fling code");
|
var fling = flingService.findFlingByShareId(userAuth.getShareId())
|
||||||
}
|
.orElseThrow();
|
||||||
|
String authCode = userAuth.getCode();
|
||||||
return makeBaseBuilder()
|
|
||||||
.setSubject("user")
|
|
||||||
.claim("sid", fling.getShareUrl())
|
|
||||||
.compact();
|
|
||||||
|
|
||||||
|
if (!flingService.hasAuthCode(fling.getId(), authCode)) {
|
||||||
|
throw new AccessDeniedException("Wrong fling code");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Authentication parseAuthentication(String token) {
|
return makeBaseBuilder()
|
||||||
Claims claims = parseClaims(token);
|
.setSubject("user")
|
||||||
|
.claim("sid", fling.getShareUrl())
|
||||||
|
.compact();
|
||||||
|
|
||||||
FlingAuthority authority;
|
}
|
||||||
Long flingId;
|
|
||||||
|
|
||||||
switch (claims.getSubject()) {
|
public Authentication parseAuthentication(String token) {
|
||||||
case "owner":
|
Claims claims = parseClaims(token);
|
||||||
authority = FlingAuthority.FLING_OWNER;
|
|
||||||
flingId = null;
|
|
||||||
break;
|
|
||||||
case "user":
|
|
||||||
authority = FlingAuthority.FLING_USER;
|
|
||||||
var sid = claims.get("sid", String.class);
|
|
||||||
flingId = flingService.findFlingByShareId(sid).orElseThrow().getId();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new BadCredentialsException("Invalid token");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FlingToken(new GrantedFlingAuthority(authority, flingId));
|
FlingAuthority authority;
|
||||||
|
Long flingId;
|
||||||
|
|
||||||
|
switch (claims.getSubject()) {
|
||||||
|
case "owner":
|
||||||
|
authority = FlingAuthority.FLING_OWNER;
|
||||||
|
flingId = null;
|
||||||
|
break;
|
||||||
|
case "user":
|
||||||
|
authority = FlingAuthority.FLING_USER;
|
||||||
|
var sid = claims.get("sid", String.class);
|
||||||
|
flingId = flingService.findFlingByShareId(sid).orElseThrow().getId();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BadCredentialsException("Invalid token");
|
||||||
}
|
}
|
||||||
|
|
||||||
private JwtBuilder makeBaseBuilder() {
|
return new FlingToken(new GrantedFlingAuthority(authority, flingId));
|
||||||
return Jwts.builder()
|
}
|
||||||
.setIssuedAt(Date.from(Instant.now()))
|
|
||||||
.setExpiration(Date.from(Instant.now().plusSeconds(securityConfig.getJwtExpiration())))
|
|
||||||
.signWith(signingKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Claims parseClaims(String token) {
|
private JwtBuilder makeBaseBuilder() {
|
||||||
return jwtParser.parseClaimsJws(token).getBody();
|
return Jwts.builder()
|
||||||
}
|
.setIssuedAt(Date.from(Instant.now()))
|
||||||
|
.setExpiration(Date.from(Instant.now().plusSeconds(securityConfig.getJwtExpiration())))
|
||||||
|
.signWith(signingKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Claims parseClaims(String token) {
|
||||||
|
return jwtParser.parseClaimsJws(token).getBody();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,34 @@
|
||||||
package net.friedl.fling.security.authentication;
|
package net.friedl.fling.security.authentication;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
|
|
||||||
public class FlingToken extends AbstractAuthenticationToken {
|
public class FlingToken extends AbstractAuthenticationToken {
|
||||||
|
|
||||||
private static final long serialVersionUID = -1112423505610346583L;
|
private static final long serialVersionUID = -1112423505610346583L;
|
||||||
private GrantedFlingAuthority grantedFlingAuthority;
|
private GrantedFlingAuthority grantedFlingAuthority;
|
||||||
|
|
||||||
public FlingToken(GrantedFlingAuthority authority) {
|
public FlingToken(GrantedFlingAuthority authority) {
|
||||||
super(List.of(authority));
|
super(List.of(authority));
|
||||||
this.grantedFlingAuthority = authority;
|
this.grantedFlingAuthority = authority;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GrantedFlingAuthority getGrantedFlingAuthority() {
|
public GrantedFlingAuthority getGrantedFlingAuthority() {
|
||||||
return this.grantedFlingAuthority;
|
return this.grantedFlingAuthority;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getCredentials() {
|
public Object getCredentials() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getPrincipal() {
|
public Object getPrincipal() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAuthenticated() {
|
public boolean isAuthenticated() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package net.friedl.fling.security.authentication;
|
package net.friedl.fling.security.authentication;
|
||||||
|
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
import net.friedl.fling.security.FlingAuthority;
|
import net.friedl.fling.security.FlingAuthority;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,23 +10,23 @@ import net.friedl.fling.security.FlingAuthority;
|
||||||
*/
|
*/
|
||||||
public class GrantedFlingAuthority implements GrantedAuthority {
|
public class GrantedFlingAuthority implements GrantedAuthority {
|
||||||
|
|
||||||
private static final long serialVersionUID = -1552301479158714777L;
|
private static final long serialVersionUID = -1552301479158714777L;
|
||||||
|
|
||||||
private FlingAuthority authority;
|
private FlingAuthority authority;
|
||||||
private Long flingId;
|
private Long flingId;
|
||||||
|
|
||||||
public GrantedFlingAuthority(FlingAuthority authority, Long flingId) {
|
public GrantedFlingAuthority(FlingAuthority authority, Long flingId) {
|
||||||
this.authority = authority;
|
this.authority = authority;
|
||||||
this.flingId = flingId;
|
this.flingId = flingId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getFlingId() {
|
public Long getFlingId() {
|
||||||
return this.flingId;
|
return this.flingId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getAuthority() {
|
public String getAuthority() {
|
||||||
return authority.name();
|
return authority.name();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,55 +1,53 @@
|
||||||
package net.friedl.fling.security.authentication;
|
package net.friedl.fling.security.authentication;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
private static final String TOKEN_PREFIX = "Bearer ";
|
private static final String TOKEN_PREFIX = "Bearer ";
|
||||||
private static final String HEADER_STRING = "Authorization";
|
private static final String HEADER_STRING = "Authorization";
|
||||||
|
|
||||||
private AuthenticationService authenticationService;
|
private AuthenticationService authenticationService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public JwtAuthenticationFilter(AuthenticationService authenticationService) {
|
public JwtAuthenticationFilter(AuthenticationService authenticationService) {
|
||||||
this.authenticationService = authenticationService;
|
this.authenticationService = authenticationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
||||||
|
FilterChain filterChain)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
String header = request.getHeader(HEADER_STRING);
|
||||||
|
|
||||||
|
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
|
||||||
|
log.warn("Could not find bearer token. No JWT authentication.");
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
String authToken = header.replace(TOKEN_PREFIX, "");
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
|
||||||
throws ServletException, IOException {
|
|
||||||
|
|
||||||
String header = request.getHeader(HEADER_STRING);
|
SecurityContext securityContext = SecurityContextHolder.getContext();
|
||||||
|
|
||||||
if(header == null || !header.startsWith(TOKEN_PREFIX)) {
|
if (securityContext.getAuthentication() == null) {
|
||||||
log.warn("Could not find bearer token. No JWT authentication.");
|
Authentication authentication = authenticationService.parseAuthentication(authToken);
|
||||||
filterChain.doFilter(request, response);
|
securityContext.setAuthentication(authentication);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String authToken = header.replace(TOKEN_PREFIX, "");
|
|
||||||
|
|
||||||
SecurityContext securityContext = SecurityContextHolder.getContext();
|
|
||||||
|
|
||||||
if(securityContext.getAuthentication() == null) {
|
|
||||||
Authentication authentication = authenticationService.parseAuthentication(authToken);
|
|
||||||
securityContext.setAuthentication(authentication);
|
|
||||||
}
|
|
||||||
|
|
||||||
filterChain.doFilter(request, response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,6 @@ import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class OwnerAuthDto {
|
public class OwnerAuthDto {
|
||||||
private String username;
|
private String username;
|
||||||
private String password;
|
private String password;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,6 @@ import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class UserAuthDto {
|
public class UserAuthDto {
|
||||||
String shareId;
|
String shareId;
|
||||||
String code;
|
String code;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,11 @@ import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import javax.transaction.Transactional;
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.json.JsonParser;
|
import org.springframework.boot.json.JsonParser;
|
||||||
import org.springframework.boot.json.JsonParserFactory;
|
import org.springframework.boot.json.JsonParserFactory;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import net.friedl.fling.model.dto.ArtifactDto;
|
import net.friedl.fling.model.dto.ArtifactDto;
|
||||||
import net.friedl.fling.model.mapper.ArtifactMapper;
|
import net.friedl.fling.model.mapper.ArtifactMapper;
|
||||||
import net.friedl.fling.persistence.archive.Archive;
|
import net.friedl.fling.persistence.archive.Archive;
|
||||||
|
@ -24,69 +21,69 @@ import net.friedl.fling.persistence.repositories.FlingRepository;
|
||||||
@Transactional
|
@Transactional
|
||||||
public class ArtifactService {
|
public class ArtifactService {
|
||||||
|
|
||||||
private FlingRepository flingRepository;
|
private FlingRepository flingRepository;
|
||||||
private ArtifactRepository artifactRepository;
|
private ArtifactRepository artifactRepository;
|
||||||
private ArtifactMapper artifactMapper;
|
private ArtifactMapper artifactMapper;
|
||||||
private Archive archive;
|
private Archive archive;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ArtifactService(ArtifactRepository artifactRepository, FlingRepository flingRepository,
|
public ArtifactService(ArtifactRepository artifactRepository, FlingRepository flingRepository,
|
||||||
ArtifactMapper artifactMapper, Archive archive) {
|
ArtifactMapper artifactMapper, Archive archive) {
|
||||||
this.artifactRepository = artifactRepository;
|
this.artifactRepository = artifactRepository;
|
||||||
this.flingRepository = flingRepository;
|
this.flingRepository = flingRepository;
|
||||||
this.artifactMapper = artifactMapper;
|
this.artifactMapper = artifactMapper;
|
||||||
this.archive = archive;
|
this.archive = archive;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ArtifactDto> findAllArtifacts(Long flingId) {
|
public List<ArtifactDto> findAllArtifacts(Long flingId) {
|
||||||
return artifactMapper.map(artifactRepository.findAllByFlingId(flingId));
|
return artifactMapper.map(artifactRepository.findAllByFlingId(flingId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArtifactDto storeArtifact(Long flingId, InputStream artifact) throws ArchiveException {
|
public ArtifactDto storeArtifact(Long flingId, InputStream artifact) throws ArchiveException {
|
||||||
var flingEntity = flingRepository.findById(flingId).orElseThrow();
|
var flingEntity = flingRepository.findById(flingId).orElseThrow();
|
||||||
var archiveId = archive.store(artifact);
|
var archiveId = archive.store(artifact);
|
||||||
|
|
||||||
ArtifactEntity artifactEntity = new ArtifactEntity();
|
ArtifactEntity artifactEntity = new ArtifactEntity();
|
||||||
artifactEntity.setDoi(archiveId);
|
artifactEntity.setDoi(archiveId);
|
||||||
artifactEntity.setFling(flingEntity);
|
artifactEntity.setFling(flingEntity);
|
||||||
|
|
||||||
artifactRepository.save(artifactEntity);
|
artifactRepository.save(artifactEntity);
|
||||||
|
|
||||||
return artifactMapper.map(artifactEntity);
|
return artifactMapper.map(artifactEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<ArtifactDto> findArtifact(Long artifactId) {
|
public Optional<ArtifactDto> findArtifact(Long artifactId) {
|
||||||
return artifactMapper.map(artifactRepository.findById(artifactId));
|
return artifactMapper.map(artifactRepository.findById(artifactId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArtifactDto mergeArtifact(Long artifactId, String body) {
|
public ArtifactDto mergeArtifact(Long artifactId, String body) {
|
||||||
JsonParser jsonParser = JsonParserFactory.getJsonParser();
|
JsonParser jsonParser = JsonParserFactory.getJsonParser();
|
||||||
Map<String, Object> parsedBody = jsonParser.parseMap(body);
|
Map<String, Object> parsedBody = jsonParser.parseMap(body);
|
||||||
|
|
||||||
artifactRepository.findById(artifactId)
|
artifactRepository.findById(artifactId)
|
||||||
// map entity to dto
|
// map entity to dto
|
||||||
.map(artifactMapper::map)
|
.map(artifactMapper::map)
|
||||||
// merge parsedBody into dto
|
// merge parsedBody into dto
|
||||||
.map(a -> artifactMapper.merge(a, parsedBody))
|
.map(a -> artifactMapper.merge(a, parsedBody))
|
||||||
// map dto to entity
|
// map dto to entity
|
||||||
.map(artifactMapper::map)
|
.map(artifactMapper::map)
|
||||||
.ifPresent(artifactRepository::save);
|
.ifPresent(artifactRepository::save);
|
||||||
|
|
||||||
return artifactMapper.map(artifactRepository.getOne(artifactId));
|
return artifactMapper.map(artifactRepository.getOne(artifactId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteArtifact(Long artifactId) throws ArchiveException {
|
public void deleteArtifact(Long artifactId) throws ArchiveException {
|
||||||
var doi = artifactRepository.getOne(artifactId).getDoi();
|
var doi = artifactRepository.getOne(artifactId).getDoi();
|
||||||
artifactRepository.deleteById(artifactId);
|
artifactRepository.deleteById(artifactId);
|
||||||
archive.remove(doi);
|
archive.remove(doi);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String generateDownloadId(Long artifactId) {
|
public String generateDownloadId(Long artifactId) {
|
||||||
// TODO: This id is not secured! Generate temporary download id
|
// TODO: This id is not secured! Generate temporary download id
|
||||||
return artifactRepository.getOne(artifactId).getDoi();
|
return artifactRepository.getOne(artifactId).getDoi();
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream downloadArtifact(String downloadId) throws ArchiveException {
|
public InputStream downloadArtifact(String downloadId) throws ArchiveException {
|
||||||
return archive.get(downloadId);
|
return archive.get(downloadId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,16 +16,13 @@ import java.util.function.Supplier;
|
||||||
import java.util.zip.Deflater;
|
import java.util.zip.Deflater;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import javax.transaction.Transactional;
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.util.Pair;
|
import org.springframework.data.util.Pair;
|
||||||
import org.springframework.security.crypto.codec.Hex;
|
import org.springframework.security.crypto.codec.Hex;
|
||||||
import org.springframework.security.crypto.keygen.KeyGenerators;
|
import org.springframework.security.crypto.keygen.KeyGenerators;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.friedl.fling.model.dto.FlingDto;
|
import net.friedl.fling.model.dto.FlingDto;
|
||||||
import net.friedl.fling.model.mapper.FlingMapper;
|
import net.friedl.fling.model.mapper.FlingMapper;
|
||||||
|
@ -40,157 +37,161 @@ import net.friedl.fling.persistence.repositories.FlingRepository;
|
||||||
@Transactional
|
@Transactional
|
||||||
public class FlingService {
|
public class FlingService {
|
||||||
|
|
||||||
private FlingRepository flingRepository;
|
private FlingRepository flingRepository;
|
||||||
private FlingMapper flingMapper;
|
private FlingMapper flingMapper;
|
||||||
private Archive archive;
|
private Archive archive;
|
||||||
private MessageDigest keyHashDigest;
|
private MessageDigest keyHashDigest;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public FlingService(FlingRepository flingRepository, FlingMapper flingMapper, Archive archive, MessageDigest keyHashDigest) {
|
public FlingService(FlingRepository flingRepository, FlingMapper flingMapper, Archive archive,
|
||||||
this.flingRepository = flingRepository;
|
MessageDigest keyHashDigest) {
|
||||||
this.flingMapper = flingMapper;
|
this.flingRepository = flingRepository;
|
||||||
this.archive = archive;
|
this.flingMapper = flingMapper;
|
||||||
this.keyHashDigest = keyHashDigest;
|
this.archive = archive;
|
||||||
|
this.keyHashDigest = keyHashDigest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<FlingDto> findAll() {
|
||||||
|
return flingMapper.map(flingRepository.findAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long createFling(FlingDto flingDto) {
|
||||||
|
if (!StringUtils.hasText(flingDto.getShareUrl())) {
|
||||||
|
flingDto.setShareUrl(generateShareUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FlingDto> findAll() {
|
var flingEntity = flingMapper.map(flingDto);
|
||||||
return flingMapper.map(flingRepository.findAll());
|
flingEntity.setAuthCode(hashKey(flingEntity.getAuthCode()));
|
||||||
}
|
flingEntity = flingRepository.save(flingEntity);
|
||||||
|
return flingEntity.getId();
|
||||||
|
}
|
||||||
|
|
||||||
public Long createFling(FlingDto flingDto) {
|
public Boolean existsShareUrl(String shareUrl) {
|
||||||
if (!StringUtils.hasText(flingDto.getShareUrl())) {
|
return !flingRepository.findByShareUrl(shareUrl).isEmpty();
|
||||||
flingDto.setShareUrl(generateShareUrl());
|
}
|
||||||
|
|
||||||
|
public void mergeFling(Long flingId, FlingDto flingDto) {
|
||||||
|
var flingEntity = flingRepository.getOne(flingId);
|
||||||
|
|
||||||
|
mergeNonEmpty(flingDto::getAllowUpload, flingEntity::setAllowUpload);
|
||||||
|
mergeNonEmpty(flingDto::getDirectDownload, flingEntity::setDirectDownload);
|
||||||
|
mergeWithEmpty(flingDto::getExpirationClicks, flingEntity::setExpirationClicks);
|
||||||
|
mergeWithEmpty(flingDto::getExpirationTime, flingEntity::setExpirationTime);
|
||||||
|
mergeNonEmpty(flingDto::getName, flingEntity::setName);
|
||||||
|
mergeNonEmpty(flingDto::getShared, flingEntity::setShared);
|
||||||
|
mergeNonEmpty(flingDto::getShareUrl, flingEntity::setShareUrl);
|
||||||
|
mergeWithEmpty(() -> hashKey(flingDto.getAuthCode()), flingEntity::setAuthCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<FlingDto> findFlingById(Long flingId) {
|
||||||
|
return flingMapper.map(flingRepository.findById(flingId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<FlingDto> findFlingByShareId(String shareUrl) {
|
||||||
|
return flingMapper.map(flingRepository.findByShareUrl(shareUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteFlingById(Long flingId) {
|
||||||
|
flingRepository.deleteById(flingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAuthCode(Long flingId, String authCode) {
|
||||||
|
var fling = flingRepository.getOne(flingId);
|
||||||
|
|
||||||
|
if (!StringUtils.hasText(fling.getAuthCode()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return fling.getAuthCode().equals(hashKey(authCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getShareName(String shareUrl) {
|
||||||
|
|
||||||
|
FlingEntity flingEntity = flingRepository.findByShareUrl(shareUrl).orElseThrow();
|
||||||
|
|
||||||
|
if (flingEntity.getArtifacts().size() > 1)
|
||||||
|
return flingEntity.getName();
|
||||||
|
else if (flingEntity.getArtifacts().size() == 1)
|
||||||
|
return flingEntity.getArtifacts().stream().findFirst().get().getName();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long countArtifacts(Long flingId) {
|
||||||
|
return flingRepository.countArtifactsById(flingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getFlingSize(Long flingId) {
|
||||||
|
var fling = flingRepository.getOne(flingId);
|
||||||
|
|
||||||
|
return fling.getArtifacts().stream()
|
||||||
|
.map(ae -> ae.getSize())
|
||||||
|
.reduce(0L, (acc, as) -> acc + as);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String packageFling(Long flingId) throws IOException, ArchiveException {
|
||||||
|
var fling = flingRepository.getOne(flingId);
|
||||||
|
var tempFile = Files.createTempFile(Long.toString(flingId), ".zip");
|
||||||
|
|
||||||
|
try (var zipStream = new ZipOutputStream(new FileOutputStream(tempFile.toFile()))) {
|
||||||
|
zipStream.setLevel(Deflater.BEST_SPEED);
|
||||||
|
for (ArtifactEntity artifactEntity : fling.getArtifacts()) {
|
||||||
|
ZipEntry ze = new ZipEntry(artifactEntity.getName());
|
||||||
|
zipStream.putNextEntry(ze);
|
||||||
|
|
||||||
|
var artifactStream = archive.get(artifactEntity.getDoi());
|
||||||
|
try (var archiveEntryStream = new BufferedInputStream(artifactStream)) {
|
||||||
|
int b;
|
||||||
|
while ((b = archiveEntryStream.read()) != -1) {
|
||||||
|
zipStream.write(b);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
zipStream.closeEntry();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
var flingEntity = flingMapper.map(flingDto);
|
|
||||||
flingEntity.setAuthCode(hashKey(flingEntity.getAuthCode()));
|
|
||||||
flingEntity = flingRepository.save(flingEntity);
|
|
||||||
return flingEntity.getId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean existsShareUrl(String shareUrl) {
|
return tempFile.getFileName().toString();
|
||||||
return !flingRepository.findByShareUrl(shareUrl).isEmpty();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void mergeFling(Long flingId, FlingDto flingDto) {
|
public Pair<InputStream, Long> downloadFling(String fileId) throws IOException, ArchiveException {
|
||||||
var flingEntity = flingRepository.getOne(flingId);
|
var tempFile = Paths.get(System.getProperty("java.io.tmpdir"), fileId).toFile();
|
||||||
|
|
||||||
mergeNonEmpty(flingDto::getAllowUpload, flingEntity::setAllowUpload);
|
var archiveLength = tempFile.length();
|
||||||
mergeNonEmpty(flingDto::getDirectDownload, flingEntity::setDirectDownload);
|
var archiveStream = new FileInputStream(tempFile);
|
||||||
mergeWithEmpty(flingDto::getExpirationClicks, flingEntity::setExpirationClicks);
|
|
||||||
mergeWithEmpty(flingDto::getExpirationTime, flingEntity::setExpirationTime);
|
|
||||||
mergeNonEmpty(flingDto::getName, flingEntity::setName);
|
|
||||||
mergeNonEmpty(flingDto::getShared, flingEntity::setShared);
|
|
||||||
mergeNonEmpty(flingDto::getShareUrl, flingEntity::setShareUrl);
|
|
||||||
mergeWithEmpty(() -> hashKey(flingDto.getAuthCode()), flingEntity::setAuthCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<FlingDto> findFlingById(Long flingId) {
|
return Pair.of(archiveStream, archiveLength);
|
||||||
return flingMapper.map(flingRepository.findById(flingId));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<FlingDto> findFlingByShareId(String shareUrl) {
|
public String generateShareUrl() {
|
||||||
return flingMapper.map(flingRepository.findByShareUrl(shareUrl));
|
var key = KeyGenerators
|
||||||
}
|
.secureRandom(16)
|
||||||
|
.generateKey();
|
||||||
|
|
||||||
public void deleteFlingById(Long flingId) {
|
return Base64.getUrlEncoder().encodeToString(key)
|
||||||
flingRepository.deleteById(flingId);
|
// replace all special chars [=-_] in RFC 4648
|
||||||
}
|
// "URL and Filename safe" table with characters from
|
||||||
|
// [A-Za-z0-9]. Hence, the generated share url will only consist
|
||||||
|
// of [A-Za-z0-9].
|
||||||
|
.replace('=', 'q')
|
||||||
|
.replace('_', 'u')
|
||||||
|
.replace('-', 'd');
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasAuthCode(Long flingId, String authCode) {
|
public String hashKey(String key) {
|
||||||
var fling = flingRepository.getOne(flingId);
|
if (!StringUtils.hasText(key))
|
||||||
|
return null;
|
||||||
|
|
||||||
if(!StringUtils.hasText(fling.getAuthCode())) return true;
|
return new String(Hex.encode(keyHashDigest.digest(key.getBytes())));
|
||||||
|
}
|
||||||
|
|
||||||
return fling.getAuthCode().equals(hashKey(authCode));
|
private <T> void mergeNonEmpty(Supplier<T> sup, Consumer<T> con) {
|
||||||
}
|
T r = sup.get();
|
||||||
|
if (r != null)
|
||||||
|
con.accept(r);
|
||||||
|
}
|
||||||
|
|
||||||
public String getShareName(String shareUrl) {
|
private <T> void mergeWithEmpty(Supplier<T> sup, Consumer<T> con) {
|
||||||
|
T r = sup.get();
|
||||||
FlingEntity flingEntity = flingRepository.findByShareUrl(shareUrl).orElseThrow();
|
con.accept(r);
|
||||||
|
}
|
||||||
if (flingEntity.getArtifacts().size() > 1)
|
|
||||||
return flingEntity.getName();
|
|
||||||
else if (flingEntity.getArtifacts().size() == 1)
|
|
||||||
return flingEntity.getArtifacts().stream().findFirst().get().getName();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long countArtifacts(Long flingId) {
|
|
||||||
return flingRepository.countArtifactsById(flingId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getFlingSize(Long flingId) {
|
|
||||||
var fling = flingRepository.getOne(flingId);
|
|
||||||
|
|
||||||
return fling.getArtifacts().stream()
|
|
||||||
.map(ae -> ae.getSize())
|
|
||||||
.reduce(0L, (acc, as) -> acc+as);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String packageFling(Long flingId) throws IOException, ArchiveException {
|
|
||||||
var fling = flingRepository.getOne(flingId);
|
|
||||||
var tempFile = Files.createTempFile(Long.toString(flingId), ".zip");
|
|
||||||
|
|
||||||
try(var zipStream = new ZipOutputStream(new FileOutputStream(tempFile.toFile()))){
|
|
||||||
zipStream.setLevel(Deflater.BEST_SPEED);
|
|
||||||
for(ArtifactEntity artifactEntity: fling.getArtifacts()) {
|
|
||||||
ZipEntry ze = new ZipEntry(artifactEntity.getName());
|
|
||||||
zipStream.putNextEntry(ze);
|
|
||||||
|
|
||||||
var artifactStream = archive.get(artifactEntity.getDoi());
|
|
||||||
try(var archiveEntryStream = new BufferedInputStream(artifactStream)) {
|
|
||||||
int b;
|
|
||||||
while( (b = archiveEntryStream.read()) != -1 ) {
|
|
||||||
zipStream.write(b);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
zipStream.closeEntry();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tempFile.getFileName().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Pair<InputStream, Long> downloadFling(String fileId) throws IOException, ArchiveException {
|
|
||||||
var tempFile = Paths.get(System.getProperty("java.io.tmpdir"), fileId).toFile();
|
|
||||||
|
|
||||||
var archiveLength = tempFile.length();
|
|
||||||
var archiveStream = new FileInputStream(tempFile);
|
|
||||||
|
|
||||||
return Pair.of(archiveStream, archiveLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String generateShareUrl() {
|
|
||||||
var key = KeyGenerators
|
|
||||||
.secureRandom(16)
|
|
||||||
.generateKey();
|
|
||||||
|
|
||||||
return Base64.getUrlEncoder().encodeToString(key)
|
|
||||||
// replace all special chars [=-_] in RFC 4648
|
|
||||||
// "URL and Filename safe" table with characters from
|
|
||||||
// [A-Za-z0-9]. Hence, the generated share url will only consist
|
|
||||||
// of [A-Za-z0-9].
|
|
||||||
.replace('=', 'q')
|
|
||||||
.replace('_', 'u')
|
|
||||||
.replace('-', 'd');
|
|
||||||
}
|
|
||||||
|
|
||||||
public String hashKey(String key) {
|
|
||||||
if(!StringUtils.hasText(key)) return null;
|
|
||||||
|
|
||||||
return new String(Hex.encode(keyHashDigest.digest(key.getBytes())));
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> void mergeNonEmpty(Supplier<T> sup, Consumer<T> con) {
|
|
||||||
T r = sup.get();
|
|
||||||
if(r != null) con.accept(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> void mergeWithEmpty(Supplier<T> sup, Consumer<T> con) {
|
|
||||||
T r = sup.get();
|
|
||||||
con.accept(r);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,287 +0,0 @@
|
||||||
---
|
|
||||||
openapi: 3.0.2
|
|
||||||
info:
|
|
||||||
title: Fling
|
|
||||||
version: 1.0.0
|
|
||||||
description: A project based API for publishing and sharing digital artifacts
|
|
||||||
contact:
|
|
||||||
name: Armin Friedl
|
|
||||||
email: dev@friedl.net
|
|
||||||
paths:
|
|
||||||
/fling:
|
|
||||||
summary: A fling share
|
|
||||||
description: |-
|
|
||||||
A grouping of shared objects. Settings for sharing and expiration are
|
|
||||||
associated with a fling.
|
|
||||||
get:
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/Fling'
|
|
||||||
description: List of metadata for all flings
|
|
||||||
summary: Get a list of metadata for all flings
|
|
||||||
post:
|
|
||||||
requestBody:
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/Fling'
|
|
||||||
required: true
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Fling created successfuly
|
|
||||||
summary: Create a new fling
|
|
||||||
/fling/{flingId}:
|
|
||||||
get:
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/Fling'
|
|
||||||
description: A fling object containing all metadata of the fling
|
|
||||||
summary: Get metadata for a fling
|
|
||||||
description: Expiration, sharing and general information of a fling.
|
|
||||||
delete:
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Fling deleted successfully
|
|
||||||
patch:
|
|
||||||
requestBody:
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/Fling'
|
|
||||||
required: true
|
|
||||||
parameters:
|
|
||||||
- name: id
|
|
||||||
description: The unique fling id
|
|
||||||
schema:
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
in: query
|
|
||||||
required: true
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Fling was successfully updated
|
|
||||||
parameters:
|
|
||||||
- name: flingId
|
|
||||||
description: Unique id of the fling
|
|
||||||
schema:
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
/f:
|
|
||||||
summary: Endpoint for accessing flings by external users
|
|
||||||
/f/{shareId}:
|
|
||||||
get:
|
|
||||||
responses:
|
|
||||||
"302":
|
|
||||||
content:
|
|
||||||
text/html:
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: |-
|
|
||||||
If the fling is marked as direct download, the user will be directly redirected
|
|
||||||
to /f/{sharId}/download which starts the fling download.
|
|
||||||
"200":
|
|
||||||
content:
|
|
||||||
application/json: {}
|
|
||||||
description: sdf
|
|
||||||
parameters:
|
|
||||||
- name: shareId
|
|
||||||
description: |-
|
|
||||||
A share id with which a fling can be uniquely identified. The share id might be
|
|
||||||
the fling id also used in /fling endpoints, another artificially generated id or
|
|
||||||
a customURL given by the fling creator
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
/fling/{flingId}/artifact:
|
|
||||||
summary: Upload an object to the fling identified by id
|
|
||||||
get:
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/Artifact'
|
|
||||||
description: A list of metadata of all objects within this fling
|
|
||||||
summary: Retrieve a list of metadata all objects within this fling
|
|
||||||
post:
|
|
||||||
requestBody:
|
|
||||||
description: "Content type is any media type from \nhttps://www.iana.org/assignments/media-types/media-types.xhtml.\n\
|
|
||||||
The content can by arbitrary payload and will be stored as-is."
|
|
||||||
content:
|
|
||||||
application/octet-stream:
|
|
||||||
schema:
|
|
||||||
format: binary
|
|
||||||
type: string
|
|
||||||
required: true
|
|
||||||
parameters:
|
|
||||||
- name: name
|
|
||||||
description: The name of the object
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
in: query
|
|
||||||
required: true
|
|
||||||
- name: path
|
|
||||||
description: |-
|
|
||||||
A path under which the object should be stored. Nested paths must be delimited
|
|
||||||
by forward slash '/'. The path might or might not start with a '/', it will always
|
|
||||||
be interpreted as absolute starting from the fling root.
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
in: query
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/Artifact'
|
|
||||||
description: Return the metadata of a fling object after successful request
|
|
||||||
parameters:
|
|
||||||
- name: flingId
|
|
||||||
description: Unique id for the fling
|
|
||||||
schema:
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
/fling/{flingId}/artifact/{objectId}:
|
|
||||||
summary: Endpoint for interacting with individual objects within a fling
|
|
||||||
get:
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/Artifact'
|
|
||||||
description: Return the metadata of a fling object after successful request
|
|
||||||
summary: Retrive the metadata of an object within a fling
|
|
||||||
put:
|
|
||||||
requestBody:
|
|
||||||
description: "Content type is any media type from \nhttps://www.iana.org/assignments/media-types/media-types.xhtml.\n\
|
|
||||||
The content can by arbitrary payload and will be stored as-is."
|
|
||||||
content:
|
|
||||||
application/octet-stream:
|
|
||||||
schema:
|
|
||||||
format: binary
|
|
||||||
type: string
|
|
||||||
required: true
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/Artifact'
|
|
||||||
description: Return the metadata of a fling object after successful request
|
|
||||||
summary: Create a new version of the object
|
|
||||||
parameters:
|
|
||||||
- name: flingId
|
|
||||||
description: The unique id of the fling
|
|
||||||
schema:
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
- name: objectId
|
|
||||||
description: |-
|
|
||||||
The unique id of the object within its fling. The id is not necessarily globally
|
|
||||||
unique across all flings.
|
|
||||||
schema:
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
components:
|
|
||||||
schemas:
|
|
||||||
Fling:
|
|
||||||
description: |-
|
|
||||||
Fling containing all metadata of a fling, including general information,
|
|
||||||
sharing settings and expiration settings.
|
|
||||||
required:
|
|
||||||
- name
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
description: Human readable name of the fling
|
|
||||||
type: string
|
|
||||||
id:
|
|
||||||
format: int64
|
|
||||||
description: Unique id of the fling
|
|
||||||
type: integer
|
|
||||||
expiration:
|
|
||||||
description: Expiration settings
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
expirationType:
|
|
||||||
description: Expiration type
|
|
||||||
enum:
|
|
||||||
- Time
|
|
||||||
- Clicks
|
|
||||||
type: string
|
|
||||||
value:
|
|
||||||
description: Parsable string representation for the expiration type
|
|
||||||
type: string
|
|
||||||
sharing:
|
|
||||||
description: Settings for sharing
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
directDownload:
|
|
||||||
description: Accessing the share will immediately prompt a download
|
|
||||||
type: boolean
|
|
||||||
upload:
|
|
||||||
description: Allow external users to upload to the share
|
|
||||||
type: boolean
|
|
||||||
customURL:
|
|
||||||
description: |-
|
|
||||||
Makes the fling available under a custom URL. The URL is only
|
|
||||||
allowed to have one path element. The allowed characters are
|
|
||||||
[A-Za-z0-9-._~], that is, the unreserved URL characters of
|
|
||||||
https://www.ietf.org/rfc/rfc3986 excluding the forward
|
|
||||||
slash. The customURL must be unique across all flings
|
|
||||||
of a Fling application deployment.
|
|
||||||
type: string
|
|
||||||
Artifact:
|
|
||||||
description: An object in a fling share
|
|
||||||
required:
|
|
||||||
- name
|
|
||||||
- doi
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
description: The name of the object
|
|
||||||
type: string
|
|
||||||
id:
|
|
||||||
format: int64
|
|
||||||
description: The unique id of the object within its fling
|
|
||||||
type: integer
|
|
||||||
path:
|
|
||||||
description: |-
|
|
||||||
The path of the object within its fling. Nested paths are separated by a forward
|
|
||||||
slash '/'. A path may or may not start with '/'. A path will always be interpreted
|
|
||||||
as absolute with the fling of the object as root.
|
|
||||||
type: string
|
|
||||||
doi:
|
|
||||||
description: "A unique and stable id for the fling object. This id is unique\
|
|
||||||
\ across all flings\nand versions of an object within a Fling deployment.\
|
|
||||||
\ A doi is never assigned twice\nby a Fling deployment, even if the object\
|
|
||||||
\ it referred to (or a version thereof) \nwas delted. A doi might not\
|
|
||||||
\ resolve to an object if the object was deleted."
|
|
||||||
type: string
|
|
||||||
version:
|
|
||||||
description: The version of the object
|
|
||||||
type: integer
|
|
||||||
fling:
|
|
||||||
format: int64
|
|
||||||
description: The id of the fling this object belongs to
|
|
||||||
type: integer
|
|
|
@ -5,9 +5,7 @@ import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
|
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
|
||||||
|
@ -16,7 +14,6 @@ import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||||
import org.springframework.context.annotation.FilterType;
|
import org.springframework.context.annotation.FilterType;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
import net.friedl.fling.model.dto.ArtifactDto;
|
import net.friedl.fling.model.dto.ArtifactDto;
|
||||||
import net.friedl.fling.service.ArtifactService;
|
import net.friedl.fling.service.ArtifactService;
|
||||||
|
|
||||||
|
@ -26,34 +23,34 @@ import net.friedl.fling.service.ArtifactService;
|
||||||
// do not try to create beans in security
|
// do not try to create beans in security
|
||||||
excludeFilters = @Filter(type = FilterType.REGEX, pattern = "net.friedl.fling.security.*"))
|
excludeFilters = @Filter(type = FilterType.REGEX, pattern = "net.friedl.fling.security.*"))
|
||||||
class ArtifactControllerTest {
|
class ArtifactControllerTest {
|
||||||
@Autowired
|
@Autowired
|
||||||
private MockMvc mvc;
|
private MockMvc mvc;
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
private ArtifactService artifactService;
|
private ArtifactService artifactService;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetArtifacts_noArtifacts_empty() throws Exception {
|
public void testGetArtifacts_noArtifacts_empty() throws Exception {
|
||||||
Long flingId = 123L;
|
Long flingId = 123L;
|
||||||
|
|
||||||
when(artifactService.findAllArtifacts(flingId)).thenReturn(List.of());
|
when(artifactService.findAllArtifacts(flingId)).thenReturn(List.of());
|
||||||
|
|
||||||
mvc.perform(get("/api/artifacts").param("flingId", flingId.toString()))
|
mvc.perform(get("/api/artifacts").param("flingId", flingId.toString()))
|
||||||
.andExpect(jsonPath("$", hasSize(0)));
|
.andExpect(jsonPath("$", hasSize(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetArtifacts_hasArtifacts_allArtifacts() throws Exception {
|
public void testGetArtifacts_hasArtifacts_allArtifacts() throws Exception {
|
||||||
Long flingId = 123L;
|
Long flingId = 123L;
|
||||||
String artifactName = "TEST";
|
String artifactName = "TEST";
|
||||||
|
|
||||||
ArtifactDto artifactDto = new ArtifactDto();
|
ArtifactDto artifactDto = new ArtifactDto();
|
||||||
artifactDto.setName(artifactName);
|
artifactDto.setName(artifactName);
|
||||||
|
|
||||||
when(artifactService.findAllArtifacts(flingId)).thenReturn(List.of(artifactDto));
|
when(artifactService.findAllArtifacts(flingId)).thenReturn(List.of(artifactDto));
|
||||||
|
|
||||||
mvc.perform(get("/api/artifacts").param("flingId", flingId.toString()))
|
mvc.perform(get("/api/artifacts").param("flingId", flingId.toString()))
|
||||||
.andExpect(jsonPath("$", hasSize(1)))
|
.andExpect(jsonPath("$", hasSize(1)))
|
||||||
.andExpect(jsonPath("$[0].name", equalTo(artifactName)));
|
.andExpect(jsonPath("$[0].name", equalTo(artifactName)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue