diff --git a/CHANGELOG.md b/CHANGELOG.md index d95bd9a..ab452c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,17 @@ # Changelog for MishkaDeveloperTools 0.1.2 +--- +- [ ] Solving the problem of creating extra `atom` in case of a mistake or an attack on the system. It could be a `security` issue, please update. +--- + - [x] Add allowed parent domain core key `Enum` derive style - [x] Add allowed parent domain core key `either` derive style - [x] Add allowed parent domain core key `equal` derive style - [x] Add allowed parent domain core key `custom` derive style - [x] Add driver for accepting `custom` function - [x] Add status to auto core key if the data of key exists do not create auto value +- [x] Add Conditional field structure `macro` (**Multiple states of a field**) - [ ] Add driver for accepting JSON -- [ ] Add Conditional field structure `macro` (**Multiple states of a field**) - [ ] Add driver for making JSON outputs - [ ] Add derives caller inside parent module - [ ] Add Supporting new `Typespecs` for `list(struct())` and previous one `struct()` diff --git a/lib/helper/derive/parser.ex b/lib/helper/derive/parser.ex index cfb8f5b..202a399 100644 --- a/lib/helper/derive/parser.ex +++ b/lib/helper/derive/parser.ex @@ -18,6 +18,10 @@ defmodule MishkaDeveloperTools.Helper.Derive.Parser do _e -> nil end + def convert_to_atom_map({:error, _, _, _} = error), do: error + + def convert_to_atom_map({:ok, map}) when is_map(map), do: convert_to_atom_map(map) + def convert_to_atom_map(map) when is_map(map) do for {key, value} <- map, into: %{}, do: {convert_key(key), convert_value(value)} end @@ -98,4 +102,15 @@ defmodule MishkaDeveloperTools.Helper.Derive.Parser do def is_data?(%{data: data, errors: errors}) do Enum.all?(Keyword.keys(errors), &(&1 in Keyword.keys(data))) end + + @doc false + def map_keys(map_data, keys) when is_map(map_data) do + case List.first(Map.keys(map_data)) do + nil -> keys + data when is_atom(data) -> keys + data when is_binary(data) -> Enum.map(keys, &Atom.to_string(&1)) + end + end + + def map_keys(_map, keys), do: keys end diff --git a/lib/macros/guarded_struct.ex b/lib/macros/guarded_struct.ex index 02004cf..84b9e4a 100644 --- a/lib/macros/guarded_struct.ex +++ b/lib/macros/guarded_struct.ex @@ -1400,15 +1400,15 @@ defmodule GuardedStruct do fields = h |> Enum.map(&elem(&1, 0)) conditionals = Enum.at(t, 7) - Parser.convert_to_atom_map(attrs) + attrs |> before_revaluation(key) + |> authorized_fields(fields, authorized) + |> required_fields(enforces) + |> Parser.convert_to_atom_map() |> auto_core_key(core_keys, type) |> domain_core_key() |> on_core_key(attrs) |> from_core_key(attrs) - # TODO: shift to top - |> authorized_fields(fields, authorized) - |> required_fields(enforces) |> conditional_fields_validating(conditionals, type, key, attrs) |> sub_fields_validating(fields, module, external, attrs, key, type) |> fields_validating(validator, module) @@ -1423,15 +1423,39 @@ defmodule GuardedStruct do defp before_revaluation(attrs, [:root]), do: attrs defp before_revaluation(attrs, key) when is_list(key) do - data = get_in(attrs, key) + data = get_in(attrs, Parser.map_keys(attrs, key)) if is_map(data), do: data, else: Map.new([{:bad_parameters, data}]) end defp before_revaluation(attrs, key) do - data = Map.get(attrs, key) + data = Map.get(attrs, Parser.map_keys(attrs, key)) if is_map(data), do: data, else: Map.new([{:bad_parameters, data}]) end + @doc false + def authorized_fields(attrs, fields, authorized) do + case check_authorized_fields(attrs, fields, authorized) do + {_, true, _} -> {:ok, attrs} + {_, false, filtered} -> {:error, :authorized_fields, filtered, :halt} + end + end + + @doc false + def required_fields({:ok, attrs}, enforces) do + with missing_keys <- Enum.reject(Parser.map_keys(attrs, enforces), &Map.has_key?(attrs, &1)), + {:missing_keys, true, _missing_keys} <- + {:missing_keys, Enum.empty?(missing_keys), missing_keys} do + {:ok, attrs} + else + {:missing_keys, false, missing_keys} -> + {:error, :required_fields, missing_keys, :halt} + end + end + + def required_fields({:error, _, _, :halt} = error, _), do: error + + defp auto_core_key({:error, _, _, :halt} = error, _, _), do: error + defp auto_core_key(attrs, core_keys, type) do reduce_attrs = Enum.filter(core_keys, fn {_key, %{type: type, values: _}} -> type == :auto end) @@ -1458,6 +1482,8 @@ defmodule GuardedStruct do {reduce_attrs, core_keys} end + defp domain_core_key({:error, _, _, :halt} = error), do: error + defp domain_core_key({attrs, core_keys}) do domain_parameters_errors = Enum.map(core_keys, fn @@ -1479,7 +1505,7 @@ defmodule GuardedStruct do else: {:error, :domain_parameters, domain_parameters_errors, :halt} end - defp on_core_key({:error, _, _, _} = error, _full_attrs), do: error + defp on_core_key({:error, _, _, :halt} = error, _full_attrs), do: error defp on_core_key({:ok, attrs, core_keys}, full_attrs) do dependent_keys_errors = check_dependent_keys(attrs, core_keys, full_attrs) @@ -1489,7 +1515,7 @@ defmodule GuardedStruct do else: {:error, :dependent_keys, dependent_keys_errors, :halt} end - defp from_core_key({:error, _, _, _} = error, _full_attrs), do: error + defp from_core_key({:error, _, _, :halt} = error, _full_attrs), do: error defp from_core_key({:ok, attrs, core_keys}, full_attrs) do reduce_attrs = @@ -1508,30 +1534,6 @@ defmodule GuardedStruct do {:ok, reduce_attrs} end - @doc false - def authorized_fields({:ok, attrs}, fields, authorized) do - case check_authorized_fields(attrs, fields, authorized) do - {_, true, _} -> {:ok, attrs} - {_, false, filtered} -> {:error, :authorized_fields, filtered, :halt} - end - end - - def authorized_fields({:error, _, _, :halt} = error, _, _), do: error - - @doc false - def required_fields({:ok, attrs}, enforces) do - with missing_keys <- Enum.reject(enforces, &Map.has_key?(attrs, &1)), - {:missing_keys, true, _missing_keys} <- - {:missing_keys, Enum.empty?(missing_keys), missing_keys} do - {:ok, attrs} - else - {:missing_keys, false, missing_keys} -> - {:error, :required_fields, missing_keys, :halt} - end - end - - def required_fields({:error, _, _, :halt} = error, _), do: error - defp conditional_fields_validating({:error, _, _, :halt} = error, _, _, _, _), do: error defp conditional_fields_validating({:ok, attrs}, conditionals, type, key, full_attrs) do @@ -2047,7 +2049,7 @@ defmodule GuardedStruct do {:authorized_fields, true, []} true -> - filtered = Enum.filter(Map.keys(attrs), &(&1 not in fields)) + filtered = Enum.filter(Map.keys(attrs), &(&1 not in Parser.map_keys(attrs, fields))) {:authorized_fields, length(filtered) == 0, filtered} end end diff --git a/test/guarded_struct_test.exs b/test/guarded_struct_test.exs index 5308040..c99c2de 100644 --- a/test/guarded_struct_test.exs +++ b/test/guarded_struct_test.exs @@ -1,7 +1,7 @@ defmodule MishkaDeveloperToolsTest.GuardedStructTest do use ExUnit.Case, async: true alias MishkaDeveloperTools.Helper.Derive.ValidationDerive - ############## (▰˘◡˘▰) GuardedStructTest Data (▰˘◡˘▰) ############## + ############# (▰˘◡˘▰) GuardedStructTest Data (▰˘◡˘▰) ############## # Store the bytecode so we can get information from it. {:module, _name, bytecode, _exports} = defmodule TestStruct do