Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:25
erlang
4201-public_key-pubkey_policy_tree_SUITE-and-tr...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 4201-public_key-pubkey_policy_tree_SUITE-and-tree-visuali.patch of Package erlang
From bdfd1616de36aec8af8d3b91962da9319c255bf0 Mon Sep 17 00:00:00 2001 From: Jakub Witczak <kuba@erlang.org> Date: Sat, 14 Oct 2023 20:18:32 +0200 Subject: [PATCH 1/2] public_key: pubkey_policy_tree_SUITE and tree visualization --- lib/public_key/test/Makefile | 3 +- .../test/pubkey_policy_tree_SUITE.erl | 350 ++++++++++++++++++ 2 files changed, 352 insertions(+), 1 deletion(-) create mode 100644 lib/public_key/test/pubkey_policy_tree_SUITE.erl diff --git a/lib/public_key/test/Makefile b/lib/public_key/test/Makefile index 57879c20e6..239e078193 100644 --- a/lib/public_key/test/Makefile +++ b/lib/public_key/test/Makefile @@ -33,7 +33,8 @@ MODULES= \ public_key_SUITE \ pbe_SUITE \ pkits_SUITE \ - pubkey_cert_SUITE + pubkey_cert_SUITE \ + pubkey_policy_tree_SUITE ERL_FILES= $(MODULES:%=%.erl) diff --git a/lib/public_key/test/pubkey_policy_tree_SUITE.erl b/lib/public_key/test/pubkey_policy_tree_SUITE.erl new file mode 100644 index 0000000000..0e4004baf8 --- /dev/null +++ b/lib/public_key/test/pubkey_policy_tree_SUITE.erl @@ -0,0 +1,350 @@ +-module(pubkey_policy_tree_SUITE). +-compile([export_all, nowarn_export_all]). + +-include_lib("stdlib/include/assert.hrl"). + +-define(PRE_SCRIPT, "<pre class=\"mermaid\">~n"). +-define(POST_SCRIPT, "~n</pre><script type=\"module\">import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';</script>"). + +%% -define(DEBUG, true). + +-ifdef(DEBUG). +-define(FWR(Fmt, Args), + begin + io:fwrite(user, "dbg: " ++ Fmt, Args) + end). +-else. +-define(FWR(Fmt, Args), ok). +-endif. + +-define(PAL_MMD(MMD), + begin + %% ?FWR("~s~n", [MMD]), + ct:log(MMD), + ?FWR("~n", []) + end). +-define(ANY_POLICY_OID, {2,5,29,32,0}). +-define(ROOT_TN, pubkey_policy_tree:root()). +-define(EMPTY_VPT, {}). +-define(ROOT_PN, + begin + {AnyPolicyNode, _} = ?ROOT_TN, + AnyPolicyNode + %% ?PN(?ANY_POLICY_OID) + end). +-define(PN(VP), pubkey_policy_tree:policy_node(VP, [], [])). +-define(PN(VP, EPS), pubkey_policy_tree:policy_node(VP, [], EPS)). + +%% Note: trees used for testing below might not make much sense from +%% RFC5280 perspective and can be improved for sure; but can be good +%% enough for checking some general tree handling code + +all() -> [add_leaves, + add_leaf_siblings, + all_leaves, + prune_leaves, + prune_tree_and_leaves, + constrained_policy_node_set, + valid_policy_node_set, + any_leaves, + prune_tree_shorter_branch, + prune_invalid_nodes]. + +any_leaves(_Config) -> + AL = + fun(Tree) -> + pubkey_policy_tree:any_leaves(Tree) + end, + ?assertEqual([], AL(?EMPTY_VPT)), + TreeWithAnyPolicyLeaf = {?ROOT_PN, + [{?PN("GOLD"), + [?PN(?ANY_POLICY_OID)]}]}, + log_tree_diagram("TreeWithAnyPolicyLeaf", TreeWithAnyPolicyLeaf), + ?assertEqual([?PN(?ANY_POLICY_OID)], AL(TreeWithAnyPolicyLeaf)), + TreeWithAnyPolicyNode1 = {?ROOT_PN, + [{?PN(?ANY_POLICY_OID), + [?PN("GOLD")]}, + {?PN("SILVER", ["A"]), + [?PN("SILVER", ["B"])]}]}, + log_tree_diagram("TreeWithAnyPolicyNode1", TreeWithAnyPolicyNode1), + ?assertEqual([], AL(TreeWithAnyPolicyNode1)), + ok. + +constrained_policy_node_set(_Config) -> + TreeWithAnyPolicyLeaf = {?ROOT_PN, + [{?PN("GOLD"), + [?PN(?ANY_POLICY_OID)]}]}, + TreeWithAnyPolicyNode1 = {?ROOT_PN, + [{?PN(?ANY_POLICY_OID), + [?PN("GOLD")]}, + {?PN("SILVER", ["A"]), + [?PN("SILVER", ["B"])]}]}, + TreeWithAnyPolicyNode2 = {?ROOT_PN, + [{?PN(?ANY_POLICY_OID), + [{?PN("GOLD", ["GOLD", "SILVER"]), + [?PN("SILVER")]}]}]}, + CS = + fun(Tree) -> + pubkey_policy_tree:constrained_policy_node_set(Tree) + end, + ?assertEqual([], CS(?EMPTY_VPT)), + ?assertEqual([?PN(?ANY_POLICY_OID)], CS(TreeWithAnyPolicyLeaf)), + ?assertEqual([?PN("SILVER", ["A"]), ?PN("GOLD")], CS(TreeWithAnyPolicyNode1)), + ?assertEqual([?PN("GOLD", ["GOLD", "SILVER"])], CS(TreeWithAnyPolicyNode2)), + ok. + +valid_policy_node_set(_Config) -> + VS = + fun(Tree) -> + pubkey_policy_tree:valid_policy_node_set(Tree) + end, + ?assertEqual([], VS(?EMPTY_VPT)), + TreeWithAnyPolicyLeaf = {?ROOT_PN, + [{?PN("GOLD"), + [?PN(?ANY_POLICY_OID)]}]}, + log_tree_diagram("TreeWithAnyPolicyLeaf", TreeWithAnyPolicyLeaf), + ?assertEqual([?PN("GOLD")], VS(TreeWithAnyPolicyLeaf)), + TreeWithAnyPolicyNode1 = {?ROOT_PN, + [{?PN(?ANY_POLICY_OID), + [?PN("GOLD")]}, + {?PN("SILVER", ["A"]), + [?PN("SILVER", ["B"])]}]}, + log_tree_diagram("TreeWithAnyPolicyNode1", TreeWithAnyPolicyNode1), + ?assertEqual([?PN(?ANY_POLICY_OID), ?PN("SILVER", ["A"]), ?PN("GOLD")], + VS(TreeWithAnyPolicyNode1)), + TreeWithAnyPolicyNode2 = {?ROOT_PN, + [{?PN(?ANY_POLICY_OID), + [{?PN("GOLD", ["GOLD", "SILVER"]), + [?PN("SILVER")]}]}]}, + log_tree_diagram("TreeWithAnyPolicyNode2", TreeWithAnyPolicyNode2), + ?assertEqual([?PN(?ANY_POLICY_OID), ?PN("GOLD", ["GOLD", "SILVER"])], + VS(TreeWithAnyPolicyNode2)), + ok. + +prune_invalid_nodes(_Config) -> + Children0 = [{?PN("GOLD"), [?PN("GOLD")]}, + {?PN("BLUE"), []}, % policy_tree_node type terminating branch - pruning target + ?PN("SILVER")], % leaf of policy_node type + Tree0 = {?ROOT_PN, Children0 ++ + [{?PN("GOLD"), [?ROOT_PN]}]}, + Tree0 = pubkey_policy_tree:prune_invalid_nodes(Tree0, []), + + {ok, Tree1} = explain(Tree0, + [{prune_invalid_nodes, [[?PN("SILVER")]]}, + {prune_invalid_nodes, [[?PN("BLUE")]]}, + {prune_invalid_nodes, [[?PN("GOLD")]]}]), + ?EMPTY_VPT = Tree1, + + %% prune node + Tree2 = {?ROOT_PN, Children0 ++ + [{?PN(?ANY_POLICY_OID), [?ROOT_PN]}]}, + {ok, Tree3} = explain(Tree2, + [{prune_invalid_nodes, [[?PN("SILVER")]]}, + {prune_invalid_nodes, [[?PN("BLUE")]]}, + {prune_invalid_nodes, [[?PN("GOLD")]]}, + {prune_invalid_nodes, [[?PN(?ANY_POLICY_OID)]]}, + {prune_invalid_nodes, [[?ROOT_PN]]}]), + Expected = {?ROOT_PN, [{?PN(?ANY_POLICY_OID), + [?ROOT_PN]}]}, + ?assertEqual(Expected, Tree3), + ok. + +prune_leaves(_Config) -> + NullVPT = pubkey_policy_tree:empty(), + ?assertEqual(NullVPT, pubkey_policy_tree:prune_leaves(NullVPT, null)), + Tree0 = {?ROOT_PN, + [{?PN("GOLD"), [?PN("GOLD"), ?PN("GOLD2")]}, + {?PN("GOLD"), [?PN(?ANY_POLICY_OID), ?PN(?ANY_POLICY_OID)]}]}, + {ok, Tree} = explain(Tree0, [{prune_leaves, ["GOLD"]}, + {prune_leaves, [?ANY_POLICY_OID]}]), + ?assertEqual({?ROOT_PN, + [{?PN("GOLD"), [?PN("GOLD2")]}, + {?PN("GOLD"), []}]}, Tree), + ok. + +prune_tree_and_leaves(_Config) -> + Tree0 = {?ROOT_PN, + [{?PN("SILVER", ["SILVER"]), + []}, + {?PN("GOLD", ["GOLD"]), + [?PN("GOLD", ["GOLD"])]}]}, + Instructions = [{prune_tree, []}, + {prune_leaves, ["GOLD"]}], + {ok, Tree} = explain(Tree0, Instructions), + Expected = {?ROOT_PN, + [{?PN("GOLD", ["GOLD"]), []}]}, + Expected = Tree, + ok. + +prune_tree_shorter_branch(_Config) -> + %% shorter branch ending with leaf is not expected to be pruned + Tree0 = {?ROOT_PN, + [{?PN("GOLD"), [?PN("GOLD")]}, + ?PN("SILVER")]}, + Instructions = [{prune_tree, []}], + {ok, Tree0} = explain(Tree0, Instructions), + ok. + +all_leaves(_Config) -> + Tree1 = {?ROOT_PN, + [{?PN("GOLD"), + [{?PN("GOLD"), []}, + {?PN("SILVER"), [?PN("GOLD"), ?PN("SILVER")]}]}, + {?PN("SILVER"), + [{?PN("GOLD"), []}, + {?PN("SILVER"), [?PN("GOLD"), ?PN("SILVER")]}]}]}, + log_tree_diagram("Tree1", Tree1), + ?assertEqual([?PN("GOLD"), ?PN("SILVER"), ?PN("GOLD"), ?PN("SILVER")], + pubkey_policy_tree:all_leaves(Tree1)), + ok. + +add_leaves(_Config) -> + RootTree = pubkey_policy_tree:root(), + AddLeavesFun1 = + fun(_) -> [?PN("GOLD"), ?PN("SILVER")] end, + AddLeavesFun2 = + fun(#{valid_policy := "SILVER"}) -> + [?PN("GOLD"), ?PN("SILVER")]; + (_) -> + [] + end, + Instructions = [{add_leaves, [AddLeavesFun1]}, + {add_leaves, [AddLeavesFun1]}, + {add_leaves, [AddLeavesFun2]}], + {ok, Tree} = explain(RootTree, Instructions), + ?assertEqual({?ROOT_PN, + [{?PN("GOLD"), + [{?PN("GOLD"), []}, + {?PN("SILVER"), [?PN("GOLD"), ?PN("SILVER")]}]}, + {?PN("SILVER"), + [{?PN("GOLD"), []}, + {?PN("SILVER"), [?PN("GOLD"), ?PN("SILVER")]}]}]}, + Tree), + ok. + +add_leaf_siblings(_Config) -> + RootTree = pubkey_policy_tree:root(), + AddLeavesFun1 = + fun(_) -> [?PN("GOLD"), ?PN("SILVER")] end, + AddLeavesFun2 = + fun(#{valid_policy := ?ANY_POLICY_OID}) -> + [?PN("GOLD"), ?PN("SILVER")]; + (_) -> + [] + end, + Instructions = [{add_leaf_siblings, [AddLeavesFun1]}, + {add_leaf_siblings, [AddLeavesFun1]}, + {add_leaf_siblings, [AddLeavesFun2]} + ], + {ok, Tree} = explain(RootTree, Instructions), + ?assertEqual({?ROOT_PN, + [?PN("GOLD"), ?PN("SILVER"), + ?PN("GOLD"), ?PN("SILVER"), + ?PN("GOLD"), ?PN("SILVER")]}, + Tree), + ok. + +%%-------------------------------------------------------------------- +%% Internal API +%%-------------------------------------------------------------------- +explain(InitTree, Instructions) -> + ct:log("=============================================~nSTEP: 0)"), + ct:log("Instructions=~n~p", [Instructions]), + ?PAL_MMD(to_mmd("0) Initial tree", InitTree)), + explain(InitTree, Instructions, 1). + +explain(Tree, [], _) -> + {ok, Tree}; +explain(Tree0, [{FunctionName, Args} | Rest], N) -> + Title = io_lib:format("~p) pubkey_policy_tree:~p()", [N, FunctionName]), + ct:log("=============================================~nSTEP: ~s", [Title]), + Tree = apply(pubkey_policy_tree, FunctionName, [Tree0 | Args]), + ?PAL_MMD(to_mmd(Title, Tree)), + explain(Tree, Rest, N+1). + +log_tree_diagram(Title, Tree) -> + ?PAL_MMD(to_mmd(Title, Tree)), + ok. + +to_mmd(Title, Tree) -> + ct:log("Diagram input tree~n~p", [Tree]), + {NodeInfo, Arrows} = traverse(Tree), + ?PRE_SCRIPT ++ + "---~ntitle: " ++ Title ++ "~n---~n" ++ + "flowchart TD~n" ++ + NodeInfo ++ "~n" ++ Arrows ++ "~n" + ?POST_SCRIPT. + +traverse(TreeNode = {_PolicyNode, _NoChildren = []}) -> + Path = [1], + NodeId = nid(Path), + ?FWR("Node (no children): ~p~n", [lists:reverse(Path)]), + {"", io_lib:format("~s~s~n", [NodeId, node_desc(NodeId, TreeNode)])}; +traverse(TreeNode = {}) -> + Path = [1], + NodeId = nid(Path), + ?FWR("NULL tree: ~p~n", [lists:reverse(Path)]), + {"", io_lib:format("~s~s~n", [NodeId, node_desc(NodeId, TreeNode)])}; +traverse(Tree) -> + traverse(Tree, [1], {#{}, []}). + +traverse(#{}, _Path, Acc) -> + ?FWR("Node (leaf found): ~p~n", [lists:reverse(_Path)]), + Acc; +traverse({_Node, _NoChildren = []}, _Path = [1], _Acc = {NodeInfo0, Arrows}) -> + %% traverse end + ?FWR("END Node (no children): ~p~n", [lists:reverse(_Path)]), + NodeInfo = maps:fold(fun(K, V, Acc) -> + Acc ++ K ++ V ++ "~n" + end, + "", NodeInfo0), + {NodeInfo, Arrows}; +traverse({_Node, _NoChildren = []}, _Path, Acc) -> + ?FWR("Node (no children): ~p~n", [lists:reverse(_Path)]), + Acc; +traverse(TreeNode = {PolicyNode, Children = [Child | Rest]}, Path, _Acc = {NodeInfo0, Arrows0}) -> + NodeId = nid(Path), + ChildId = nid([length(Children) | Path]), + Fmt = "~s --> ~s~n", + Args = [NodeId, ChildId], + Arrows1 = Arrows0 ++ io_lib:format(Fmt, Args), + NodeInfo = store_new_nodes([{NodeId, TreeNode}, {ChildId, Child}], NodeInfo0), + Acc2 = traverse(Child, [length(Children) | Path], {NodeInfo, Arrows1}), % traverse vertically + traverse({PolicyNode, Rest}, Path, Acc2). % traverse horizontally + +store_new_nodes([], NodeInfo) -> + NodeInfo; +store_new_nodes([{Id, Node} | Rest] , NodeInfo0) -> + NodeInfo = case maps:is_key(Id, NodeInfo0) of + true -> + NodeInfo0; + false -> + NodeDesc = node_desc(Id, Node), + NodeInfo0#{Id => NodeDesc} + end, + store_new_nodes(Rest, NodeInfo). + +node_desc(Path, {}) -> + io_lib:format("[\"(NULL) ~s\"]", [Path]); +node_desc(Path, #{valid_policy := VP, expected_policy_set := EPS}) -> + io_lib:format("[\"(L) ~s\nvp: ~s\neps: ~s\"]", [Path, p(VP), ps(EPS)]); +node_desc(Path, {#{valid_policy := VP, expected_policy_set := EPS}, _}) -> + io_lib:format("[\"(N) ~s\nvp: ~s\neps: ~s\"]", [Path, p(VP), ps(EPS)]). + +nid(Path) -> + nid(Path, ""). + +nid([], Acc) -> + "N_" ++ lists:reverse(Acc); +nid([Node], Acc) -> + nid([], Acc ++ integer_to_list(Node)); +nid([Node | Rest], Acc) -> + nid(Rest, Acc ++ integer_to_list(Node) ++ "-"). + +ps(ExpectedPolicySet) -> + [io_lib:format("~s ", [p(P)]) || P <- ExpectedPolicySet]. + +p(?ANY_POLICY_OID) -> + "anyPolicy"; +p(P) -> + P. -- 2.35.3
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor