From 90545b05cd50ae3ab253e3fff3275417ed56cb5d Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sat, 12 Jan 2019 13:52:35 +0100 Subject: [PATCH 1/7] nacm testcases for modules and datastore --- test/test_nacm.sh | 16 +- test/test_nacm_datanode.sh | 230 +++++++++++++++++++++++++++++ test/test_nacm_ext.sh | 21 +-- test/test_nacm_module.sh | 187 +++++++++++++++++++++++ test/test_nacm_protocol.sh | 30 ++-- yang/clixon-config@2018-10-21.yang | 4 +- 6 files changed, 455 insertions(+), 33 deletions(-) create mode 100755 test/test_nacm_datanode.sh create mode 100755 test/test_nacm_module.sh diff --git a/test/test_nacm.sh b/test/test_nacm.sh index aac4be87..82337442 100755 --- a/test/test_nacm.sh +++ b/test/test_nacm.sh @@ -16,7 +16,7 @@ cat < $cfg $cfg /usr/local/share/clixon - /usr/local/share/clixon + $fyang /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/restconf /usr/local/lib/$APPNAME/cli @@ -103,16 +103,16 @@ RULES=$(cat <$RULES]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" 0 "$RULES]]>]]>" "^]]>]]>$" new "commit it" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" new2 "auth get (no user: access denied)" expecteq "$(curl -sS -X GET -H \"Accept:\ application/yang-data+json\" http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' diff --git a/test/test_nacm_datanode.sh b/test/test_nacm_datanode.sh new file mode 100755 index 00000000..8bf65f56 --- /dev/null +++ b/test/test_nacm_datanode.sh @@ -0,0 +1,230 @@ +#!/bin/bash +# Authentication and authorization and IETF NACM +# NACM data node rule +# @see RFC 8341 A.1 and A.4 (and permit-all from A.2) +# Tests for: +# deny-nacm: This rule denies the "guest" group any access to the +# /nacm subtree. +# permit-acme-config: This rule gives the "limited" group read-write +# access to the acme . +# permit-dummy-interface: This rule gives the "limited" and "guest" +# groups read-update access to the acme entry named +# "dummy". This entry cannot be created or deleted by these groups; +# it can only be altered. +# permit-interface: This rule gives the "admin" group read-write +# access to all acme entries. + +APPNAME=example +# include err() and new() functions and creates $dir +. ./lib.sh +. ./nacm.sh + +cfg=$dir/conf_yang.xml +fyang=$dir/test.yang +fyangerr=$dir/err.yang + +cat < $cfg + + $cfg + /usr/local/share/clixon + $fyang + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/restconf + /usr/local/lib/$APPNAME/cli + $APPNAME + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + 1 + /usr/local/var/$APPNAME + /usr/local/lib/xmldb/text.so + false + internal + +EOF + +cat < $fyang +module $APPNAME{ + yang-version 1.1; + namespace "urn:example:clixon"; + prefix ex; + import ietf-netconf-acm { + prefix nacm; + } + leaf x{ + type int32; + description "something to edit"; + } +} +EOF + +# The groups are slightly modified from RFC8341 A.1 +# The rule-list is from A.2 +RULES=$(cat < + false + deny + deny + deny + + $NGROUPS + + + guest-acl + guest + + + deny-nacm + + /n:nacm + + * + deny + + Deny the 'guest' group any access to the /nacm data. + + + + + + limited-acl + limited + + + permit-acme-config + + /acme:acme-netconf/acme:config-parameters + + + read create update delete + + permit + + Allow the 'limited' group complete access to the acme + NETCONF configuration parameters. Showing long form + of 'access-operations' instead of shorthand. + + + + + guest-limited-acl + guest + limited + + + permit-dummy-interface + + /acme:interfaces/acme:interface[acme:name='dummy'] + + read update + permit + + Allow the 'limited' and 'guest' groups read + and update access to the dummy interface. + + + + + admin-acl + admin + + permit-interface + + /acme:interfaces/acme:interface + + * + permit + + Allow the 'admin' group full access to all acme interfaces. + + + + + $NADMIN + + + 0 +EOF +) + +# kill old backend (if any) +new "kill old backend" +sudo clixon_backend -zf $cfg +if [ $? -ne 0 ]; then + err +fi + +new "start backend -s init -f $cfg" +# start new backend +sudo $clixon_backend -s init -f $cfg +if [ $? -ne 0 ]; then + err +fi + +new "kill old restconf daemon" +sudo pkill -u www-data -f "/www-data/clixon_restconf" + +sleep 1 +new "start restconf daemon (-a is enable basic authentication)" +sudo su -c "$clixon_restconf -f $cfg -D $DBG -- -a" -s /bin/sh www-data & + +sleep $RCWAIT + +new "auth set authentication config" +expecteof "$clixon_netconf -qf $cfg" 0 "$RULES]]>]]>" "^]]>]]>$" + +new "commit it" +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" + +new "enable nacm" +expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" "" + +#--------------- nacm enabled + +new2 "auth get (wrong passwd: access denied)" +expecteq "$(curl -u andy:foo -sS -X GET http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' + +new2 "auth get (access)" +expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0} + ' + +#----------------Enable NACM + +new "enable nacm" +expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" "" + +new2 "admin get nacm" +expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0} + ' + +new2 "limited get nacm" +expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0} + ' + +new2 "guest get nacm" +expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' + +new "admin edit nacm" +expecteq "$(curl -u andy:bar -sS -X PUT -d '{"x": 1}' http://localhost/restconf/data/example:x)" "" + +new2 "limited edit nacm" +expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' + +new2 "guest edit nacm" +expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' + +new "Kill restconf daemon" +sudo pkill -u www-data -f "/www-data/clixon_restconf" + +new "Kill backend" +# Check if premature kill +pid=`pgrep -u root -f clixon_backend` +if [ -z "$pid" ]; then + err "backend already dead" +fi +# kill backend +sudo clixon_backend -z -f $cfg +if [ $? -ne 0 ]; then + err "kill backend" +fi + +rm -rf $dir diff --git a/test/test_nacm_ext.sh b/test/test_nacm_ext.sh index edbef77f..5e2ad832 100755 --- a/test/test_nacm_ext.sh +++ b/test/test_nacm_ext.sh @@ -20,6 +20,7 @@ cat < $cfg $cfg /usr/local/share/example/yang /usr/local/share/clixon + $fyang /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/backend example_backend.so$ @@ -134,7 +135,7 @@ cat < $nacmfile EOF -new "test params: -f $cfg -y $fyang" +new "test params: -f $cfg" if [ $BE -ne 0 ]; then new "kill old backend -zf $cfg " @@ -143,9 +144,9 @@ if [ $BE -ne 0 ]; then err fi sleep 1 - new "start backend -s init -f $cfg -y $fyang" + new "start backend -s init -f $cfg" # start new backend - sudo $clixon_backend -s init -f $cfg -y $fyang -D $DBG + sudo $clixon_backend -s init -f $cfg -D $DBG if [ $? -ne 0 ]; then err fi @@ -155,7 +156,7 @@ new "kill old restconf daemon" sudo pkill -u www-data -f "/www-data/clixon_restconf" new "start restconf daemon (-a is enable http basic auth)" -sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG -- -a" -s /bin/sh www-data & +sudo su -c "$clixon_restconf -f $cfg -D $DBG -- -a" -s /bin/sh www-data & sleep $RCWAIT @@ -200,22 +201,22 @@ new2 "guest edit nacm" expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}} ' new "cli show conf as admin" -expectfn "$clixon_cli -1 -U andy -l o -f $cfg -y $fyang show conf" 0 "^x 1;$" +expectfn "$clixon_cli -1 -U andy -l o -f $cfg show conf" 0 "^x 1;$" new "cli show conf as limited" -expectfn "$clixon_cli -1 -U wilma -l o -f $cfg -y $fyang show conf" 0 "^x 1;$" +expectfn "$clixon_cli -1 -U wilma -l o -f $cfg show conf" 0 "^x 1;$" new "cli show conf as guest" -expectfn "$clixon_cli -1 -U guest -l o -f $cfg -y $fyang show conf" 255 "protocol access-denied" +expectfn "$clixon_cli -1 -U guest -l o -f $cfg show conf" 255 "protocol access-denied" new "cli rpc as admin" -expectfn "$clixon_cli -1 -U andy -l o -f $cfg -y $fyang rpc ipv4" 0 "next-hop-list 2.3.4.5;" +expectfn "$clixon_cli -1 -U andy -l o -f $cfg rpc ipv4" 0 "next-hop-list 2.3.4.5;" new "cli rpc as limited" -expectfn "$clixon_cli -1 -U wilma -l o -f $cfg -y $fyang rpc ipv4" 255 "protocol access-denied default deny" +expectfn "$clixon_cli -1 -U wilma -l o -f $cfg rpc ipv4" 255 "protocol access-denied default deny" new "cli rpc as guest" -expectfn "$clixon_cli -1 -U guest -l o -f $cfg -y $fyang rpc ipv4" 255 "protocol access-denied access denied" +expectfn "$clixon_cli -1 -U guest -l o -f $cfg rpc ipv4" 255 "protocol access-denied access denied" new "Kill restconf daemon" sudo pkill -u www-data -f "/www-data/clixon_restconf" diff --git a/test/test_nacm_module.sh b/test/test_nacm_module.sh new file mode 100755 index 00000000..e2559feb --- /dev/null +++ b/test/test_nacm_module.sh @@ -0,0 +1,187 @@ +#!/bin/bash +# Authentication and authorization and IETF NACM +# NACM module rules +# @see test_nacm.sh is slightly modified - this follows the RFC more closely +# See RFC 8341 A.1 and A.2 +# Tests for: +# deny-ncm: This rule prevents the "guest" group from reading any +# monitoring information in the "ietf-netconf-monitoring" YANG +# module. +# permit-ncm: This rule allows the "limited" group to read the +# "ietf-netconf-monitoring" YANG module. +# permit-exec: This rule allows the "limited" group to invoke any +# protocol operation supported by the server. +# permit-all: This rule allows the "admin" group complete access to +# all content in the server. No subsequent rule will match for the +# "admin" group because of this module rule + +APPNAME=example +# include err() and new() functions and creates $dir +. ./lib.sh +. ./nacm.sh + +cfg=$dir/conf_yang.xml +fyang=$dir/test.yang +fyangerr=$dir/err.yang + +cat < $cfg + + $cfg + /usr/local/share/clixon + $fyang + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/restconf + /usr/local/lib/$APPNAME/cli + $APPNAME + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + 1 + /usr/local/var/$APPNAME + /usr/local/lib/xmldb/text.so + false + internal + +EOF + +cat < $fyang +module $APPNAME{ + yang-version 1.1; + namespace "urn:example:clixon"; + prefix ex; + import ietf-netconf-acm { + prefix nacm; + } + leaf x{ + type int32; + description "something to edit"; + } +} +EOF + +# The groups are slightly modified from RFC8341 A.1 ($USER added in admin group) +# The rule-list is from A.2 +RULES=$(cat < + false + deny + deny + deny + + $NGROUPS + + + guest-acl + guest + + deny-ncm + ietf-netconf-monitoring + * + deny + + Do not allow guests any access to the NETCONF + monitoring information. + + + + + limited-acl + limited + + permit-ncm + get + ietf-netconf-monitoring + read + permit + + Allow read access to the NETCONF monitoring information. + + + + permit-exec + * + exec + permit + + Allow invocation of the supported server operations. + + + + + $NADMIN + + + 0 +EOF +) + +# kill old backend (if any) +new "kill old backend" +sudo clixon_backend -zf $cfg +if [ $? -ne 0 ]; then + err +fi + +new "start backend -s init -f $cfg" +# start new backend +sudo $clixon_backend -s init -f $cfg +if [ $? -ne 0 ]; then + err +fi + +new "kill old restconf daemon" +sudo pkill -u www-data -f "/www-data/clixon_restconf" + +sleep 1 +new "start restconf daemon (-a is enable basic authentication)" +sudo su -c "$clixon_restconf -f $cfg -D $DBG -- -a" -s /bin/sh www-data & + +sleep $RCWAIT + +new "auth set authentication config" +expecteof "$clixon_netconf -qf $cfg" 0 "$RULES]]>]]>" "^]]>]]>$" + +new "commit it" +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" + +new "enable nacm" +expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" "" + +#--------------- nacm enabled + +# Read monitoring information from ietf-netconf-monitoring +new2 "admin get nacm" +expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0} + ' + +new2 "limited get nacm" +expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0} + ' + +new2 "guest get nacm" +expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' + +new "admin edit nacm" +expecteq "$(curl -u andy:bar -sS -X PUT -d '{"example:x": 1}' http://localhost/restconf/data/example:x)" "" + +new2 "limited edit nacm" +expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"example:x": 2}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' + +new2 "guest edit nacm" +expecteq "$(curl -u guest:bar -sS -X PUT -d '{"example:x": 3}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' + +new "Kill restconf daemon" +sudo pkill -u www-data -f "/www-data/clixon_restconf" + +new "Kill backend" +# Check if premature kill +pid=`pgrep -u root -f clixon_backend` +if [ -z "$pid" ]; then + err "backend already dead" +fi +# kill backend +sudo clixon_backend -z -f $cfg +if [ $? -ne 0 ]; then + err "kill backend" +fi + +rm -rf $dir diff --git a/test/test_nacm_protocol.sh b/test/test_nacm_protocol.sh index 8a3c298e..c0e89440 100755 --- a/test/test_nacm_protocol.sh +++ b/test/test_nacm_protocol.sh @@ -36,7 +36,7 @@ cat < $cfg $cfg /usr/local/share/clixon - /usr/local/share/clixon + $fyang /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/restconf /usr/local/lib/$APPNAME/cli @@ -125,17 +125,17 @@ RULES=$(cat <$RULES]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" 0 "$RULES]]>]]>" "^]]>]]>$" new "commit it" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" new "enable nacm" expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" "" +#--------------- nacm enabled + new2 "admin get nacm" expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0} ' # Rule 1: deny-kill-session new "deny-kill-session: limited fail (netconf)" -expecteof "$clixon_netconf -qf $cfg -y $fyang -U wilma" 0 "44]]>]]>" "^protocolaccess-deniederroraccess denied]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -U wilma" 0 "44]]>]]>" "^protocolaccess-deniederroraccess denied]]>]]>$" new "deny-kill-session: guest fail (netconf)" -expecteof "$clixon_netconf -qf $cfg -y $fyang -U guest" 0 "44]]>]]>" "^protocolaccess-deniederroraccess denied]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -U guest" 0 "44]]>]]>" "^protocolaccess-deniederroraccess denied]]>]]>$" new "deny-kill-session: admin ok (netconf)" -expecteof "$clixon_netconf -qf $cfg -y $fyang -U andy" 0 "44]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -U andy" 0 "44]]>]]>" "^]]>]]>$" # Rule 2: deny-delete-config new "deny-delete-config: limited fail (netconf)" -expecteof "$clixon_netconf -qf $cfg -y $fyang -U wilma" 0 "]]>]]>" "^protocolaccess-deniederroraccess denied]]>]]>$" +expecteof "$clixon_netconf -qf $cfg -U wilma" 0 "]]>]]>" "^protocolaccess-deniederroraccess denied]]>]]>$" new2 "deny-delete-config: guest fail (restconf)" expecteq "$(curl -u guest:bar -sS -X DELETE http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' @@ -193,10 +195,10 @@ expecteq "$(curl -u andy:bar -sS -X DELETE http://localhost/restconf/data)" '' # Here the whole config is gone so we need to start again new "auth set authentication config (restart)" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "$RULES]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" 0 "$RULES]]>]]>" "^]]>]]>$" new "commit it" -expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" new "enable nacm" expecteq "$(curl -u andy:bar -sS -X PUT -d '{"ietf-netconf-acm:enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" "" diff --git a/yang/clixon-config@2018-10-21.yang b/yang/clixon-config@2018-10-21.yang index a9ad7414..90c6c1bf 100644 --- a/yang/clixon-config@2018-10-21.yang +++ b/yang/clixon-config@2018-10-21.yang @@ -145,7 +145,9 @@ module clixon-config { leaf CLICON_YANG_MAIN_FILE { type string; description - "If specified load a yang module in a specific absolute filename"; + "If specified load a yang module in a specific absolute filename. + This corresponds to the -y command-line option in most CLixon + programs."; } leaf CLICON_YANG_MAIN_DIR { type string; From 04bb05c83f8fea91462776eb2ad2f3010b644462 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Tue, 22 Jan 2019 13:41:03 +0100 Subject: [PATCH 2/7] Upgrade testcases for nacm --- apps/restconf/restconf_methods.c | 4 +- example/example_backend_nacm.c | 2 +- test/test_nacm.sh | 31 ++++++------ test/test_nacm_datanode.sh | 40 ++++++++------- test/test_nacm_ext.sh | 26 +++++----- test/test_nacm_module.sh | 85 +++++++++++++++++++------------- test/test_nacm_protocol.sh | 23 +++++---- 7 files changed, 121 insertions(+), 90 deletions(-) diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index 84289dd4..27512442 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -1676,7 +1676,7 @@ api_operations_post(clicon_handle h, if (xml_parse_string(cbuf_get(cbret), NULL, &xret) < 0) goto done; /* Local error: return it and quit */ - if ((xe = xpath_first(xret, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){ if (api_return_err(h, r, xe, pretty, use_xml) < 0) goto done; goto ok; @@ -1685,7 +1685,7 @@ api_operations_post(clicon_handle h, else { /* Send to backend */ if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0) goto done; - if ((xe = xpath_first(xret, "rpc-error")) != NULL){ + if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){ if (api_return_err(h, r, xe, pretty, use_xml) < 0) goto done; goto ok; diff --git a/example/example_backend_nacm.c b/example/example_backend_nacm.c index 608916a8..eae31132 100644 --- a/example/example_backend_nacm.c +++ b/example/example_backend_nacm.c @@ -75,7 +75,7 @@ nacm_statedata(clicon_handle h, cxobj **xvec = NULL; /* Example of (static) statedata, real code would poll state */ - if (xml_parse_string("" + if (xml_parse_string("" "0" "0" "0" diff --git a/test/test_nacm.sh b/test/test_nacm.sh index b8ed20cb..460fec59 100755 --- a/test/test_nacm.sh +++ b/test/test_nacm.sh @@ -9,8 +9,7 @@ APPNAME=example . ./nacm.sh cfg=$dir/conf_yang.xml -fyang=$dir/test.yang -fyangerr=$dir/err.yang +fyang=$dir/nacm-example.yang cat < $cfg @@ -23,6 +22,7 @@ cat < $cfg /usr/local/lib/$APPNAME/cli $APPNAME /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/lib/$APPNAME/backend /usr/local/var/$APPNAME/$APPNAME.pidfile 1 /usr/local/var/$APPNAME @@ -33,10 +33,13 @@ cat < $cfg EOF cat < $fyang -module $APPNAME{ +module nacm-example{ yang-version 1.1; - namespace "urn:example:clixon"; - prefix ex; + namespace "urn:example:nacm"; + prefix nacm; + import clixon-example { + prefix ex; + } import ietf-netconf-acm { prefix nacm; } @@ -100,7 +103,7 @@ RULES=$(cat < - 0 + 0 EOF ) @@ -132,7 +135,7 @@ new "restconf DELETE whole datastore" expecteq "$(curl -u andy:bar -sS -X DELETE http://localhost/restconf/data)" "" new2 "auth get" -expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" 'null +expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 'null ' new "auth set authentication config" @@ -148,7 +151,7 @@ new2 "auth get (wrong passwd: access denied)" expecteq "$(curl -u andy:foo -sS -X GET http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' new2 "auth get (access)" -expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0} +expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" '{"nacm-example:x": 0} ' #----------------Enable NACM @@ -157,24 +160,24 @@ new "enable nacm" expecteq "$(curl -u andy:bar -sS -X PUT -d '{"ietf-netconf-acm:enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" "" new2 "admin get nacm" -expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0} +expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" '{"nacm-example:x": 0} ' new2 "limited get nacm" -expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0} +expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" '{"nacm-example:x": 0} ' new2 "guest get nacm" -expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}} ' +expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}} ' new "admin edit nacm" -expecteq "$(curl -u andy:bar -sS -X PUT -d '{"example:x": 1}' http://localhost/restconf/data/example:x)" "" +expecteq "$(curl -u andy:bar -sS -X PUT -d '{"nacm-example:x": 1}' http://localhost/restconf/data/nacm-example:x)" "" new2 "limited edit nacm" -expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"example:x": 2}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' +expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"nacm-example:x": 2}' http://localhost/restconf/data/nacm-example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' new2 "guest edit nacm" -expecteq "$(curl -u guest:bar -sS -X PUT -d '{"example:x": 3}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}} ' +expecteq "$(curl -u guest:bar -sS -X PUT -d '{"nacm-example:x": 3}' http://localhost/restconf/data/nacm-example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}} ' new "Kill restconf daemon" sudo pkill -u www-data -f "/www-data/clixon_restconf" diff --git a/test/test_nacm_datanode.sh b/test/test_nacm_datanode.sh index cd53c4dd..f6aa50b4 100755 --- a/test/test_nacm_datanode.sh +++ b/test/test_nacm_datanode.sh @@ -21,7 +21,6 @@ APPNAME=example cfg=$dir/conf_yang.xml fyang=$dir/test.yang -fyangerr=$dir/err.yang cat < $cfg @@ -34,6 +33,7 @@ cat < $cfg /usr/local/lib/$APPNAME/cli $APPNAME /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/lib/$APPNAME/backend /usr/local/var/$APPNAME/$APPNAME.pidfile 1 /usr/local/var/$APPNAME @@ -44,10 +44,13 @@ cat < $cfg EOF cat < $fyang -module $APPNAME{ +module nacm-example{ yang-version 1.1; - namespace "urn:example:clixon"; - prefix ex; + namespace "urn:example:nacm"; + prefix nacm; + import clixon-example { + prefix ex; + } import ietf-netconf-acm { prefix nacm; } @@ -146,21 +149,20 @@ RULES=$(cat <0 EOF ) - exit # XXX +new "test params: -f $cfg" -# kill old backend (if any) -new "kill old backend" -sudo clixon_backend -zf $cfg -if [ $? -ne 0 ]; then - err -fi - -new "start backend -s init -f $cfg" -# start new backend -sudo $clixon_backend -s init -f $cfg -if [ $? -ne 0 ]; then - err +if [ $BE -ne 0 ]; then + new "kill old backend" + sudo clixon_backend -zf $cfg + if [ $? -ne 0 ]; then + err + fi + new "start backend -s init -f $cfg" + sudo $clixon_backend -s init -f $cfg -D $DBG + if [ $? -ne 0 ]; then + err + fi fi new "kill old restconf daemon" @@ -218,6 +220,10 @@ expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf new "Kill restconf daemon" sudo pkill -u www-data -f "/www-data/clixon_restconf" +if [ $BE -eq 0 ]; then + exit # BE +fi + new "Kill backend" # Check if premature kill pid=`pgrep -u root -f clixon_backend` diff --git a/test/test_nacm_ext.sh b/test/test_nacm_ext.sh index 2689c8e4..f0fdf0a5 100755 --- a/test/test_nacm_ext.sh +++ b/test/test_nacm_ext.sh @@ -10,8 +10,7 @@ APPNAME=example . ./nacm.sh cfg=$dir/conf_yang.xml -fyang=$dir/test.yang -fyangerr=$dir/err.yang +fyang=$dir/nacm-example.yang nacmfile=$dir/nacmfile # Note filter out example_backend_nacm.so in CLICON_BACKEND_REGEXP below @@ -39,13 +38,13 @@ cat < $cfg EOF cat < $fyang -module $APPNAME{ +module nacm-example{ yang-version 1.1; - namespace "urn:example:my"; + namespace "urn:example:nacm"; import clixon-example { prefix ex; } - prefix my; + prefix nacm; container authentication { description "Example code for enabling www basic auth and some example users"; @@ -124,6 +123,7 @@ cat < $nacmfile $NADMIN + 0 EOF new "test params: -f $cfg" @@ -159,7 +159,7 @@ expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-ex ' new "Set x to 0" -expecteq "$(curl -u andy:bar -sS -X PUT -d '{"example:x": 0}' http://localhost/restconf/data/example:x)" "" +expecteq "$(curl -u andy:bar -sS -X PUT -d '{"nacm-example:x": 0}' http://localhost/restconf/data/nacm-example:x)" "" new2 "auth get (no user: access denied)" expecteq "$(curl -sS -X GET -H \"Accept:\ application/yang-data+json\" http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' @@ -168,28 +168,28 @@ new2 "auth get (wrong passwd: access denied)" expecteq "$(curl -u andy:foo -sS -X GET http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "The requested URL was unauthorized"}}} ' new2 "auth get (access)" -expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0} +expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" '{"nacm-example:x": 0} ' new2 "admin get nacm" -expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0} +expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" '{"nacm-example:x": 0} ' new2 "limited get nacm" -expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0} +expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" '{"nacm-example:x": 0} ' new2 "guest get nacm" -expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}} ' +expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}} ' new "admin edit nacm" -expecteq "$(curl -u andy:bar -sS -X PUT -d '{"example:x": 1}' http://localhost/restconf/data/example:x)" "" +expecteq "$(curl -u andy:bar -sS -X PUT -d '{"nacm-example:x": 1}' http://localhost/restconf/data/nacm-example:x)" "" new2 "limited edit nacm" -expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' +expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/nacm-example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' new2 "guest edit nacm" -expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}} ' +expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/nacm-example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "access denied"}}} ' new "cli show conf as admin" expectfn "$clixon_cli -1 -U andy -l o -f $cfg show conf" 0 "^x 1;$" diff --git a/test/test_nacm_module.sh b/test/test_nacm_module.sh index a991e7c9..0ac5f9d5 100755 --- a/test/test_nacm_module.sh +++ b/test/test_nacm_module.sh @@ -3,7 +3,9 @@ # NACM module rules # @see test_nacm.sh is slightly modified - this follows the RFC more closely # See RFC 8341 A.1 and A.2 -# Tests for: +# Note: use clixon-example instead of ietf-netconf-monitoring since the latter is +# not yet implemented +# Tests for # deny-ncm: This rule prevents the "guest" group from reading any # monitoring information in the "ietf-netconf-monitoring" YANG # module. @@ -21,10 +23,7 @@ APPNAME=example . ./nacm.sh cfg=$dir/conf_yang.xml -fyang=$dir/test.yang -fyangerr=$dir/err.yang - -exit # XXX +fyang=$dir/nacm-example.yang cat < $cfg @@ -37,6 +36,7 @@ cat < $cfg /usr/local/lib/$APPNAME/cli $APPNAME /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/lib/$APPNAME/backend /usr/local/var/$APPNAME/$APPNAME.pidfile 1 /usr/local/var/$APPNAME @@ -47,10 +47,13 @@ cat < $cfg EOF cat < $fyang -module $APPNAME{ +module nacm-example{ yang-version 1.1; - namespace "urn:example:clixon"; - prefix ex; + namespace "urn:example:nacm"; + prefix nacm; + import clixon-example { + prefix ex; + } import ietf-netconf-acm { prefix nacm; } @@ -72,6 +75,7 @@ RULES=$(cat < guest-acl guest @@ -113,22 +117,24 @@ RULES=$(cat < - 0 + 42 + key42val42 EOF ) -# kill old backend (if any) -new "kill old backend" -sudo clixon_backend -zf $cfg -if [ $? -ne 0 ]; then - err -fi +new "test params: -f $cfg" -new "start backend -s init -f $cfg" -# start new backend -sudo $clixon_backend -s init -f $cfg -if [ $? -ne 0 ]; then - err +if [ $BE -ne 0 ]; then + new "kill old backend" + sudo clixon_backend -zf $cfg + if [ $? -ne 0 ]; then + err + fi + new "start backend -s init -f $cfg" + sudo $clixon_backend -s init -f $cfg -D $DBG + if [ $? -ne 0 ]; then + err + fi fi new "kill old restconf daemon" @@ -151,30 +157,43 @@ expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localh #--------------- nacm enabled -# Read monitoring information from ietf-netconf-monitoring -new2 "admin get nacm" -expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0} +# Read monitoring information from example - (ietf-netconf-monitoring) +new2 "admin read ok" +expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" '{"clixon-example:translate": [{"k": "key42","value": "val42"}]} ' -new2 "limited get nacm" -expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0} +new2 "limit read ok" +expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" '{"clixon-example:translate": [{"k": "key42","value": "val42"}]} ' -new2 "guest get nacm" -expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' +new2 "guest read fail" +expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' -new "admin edit nacm" -expecteq "$(curl -u andy:bar -sS -X PUT -d '{"example:x": 1}' http://localhost/restconf/data/example:x)" "" +new2 "guest read other module" +expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' -new2 "limited edit nacm" -expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"example:x": 2}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' +new2 "admin read other module OK" +expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" '{"nacm-example:x": 42} + ' -new2 "guest edit nacm" -expecteq "$(curl -u guest:bar -sS -X PUT -d '{"example:x": 3}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' +new2 "admin rpc ok" +expecteq "$(curl -u andy:bar -s -X POST -d '{"clixon-example:input":{"x":42}}' http://localhost/restconf/operations/clixon-example:example)" '{"clixon-example:output": {"x": "42","y": "42"}} + ' + +new2 "limit rpc ok" +expecteq "$(curl -u wilma:bar -s -X POST http://localhost/restconf/operations/clixon-example:example -d '{"clixon-example:input":{"x":42}}' )" '{"clixon-example:output": {"x": "42","y": "42"}} + ' + +new2 "guest rpc fail" +expecteq "$(curl -u guest:bar -s -X POST http://localhost/restconf/operations/clixon-example:example -d '{"clixon-example:input":{"x":42}}' )" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' new "Kill restconf daemon" sudo pkill -u www-data -f "/www-data/clixon_restconf" +if [ $BE -eq 0 ]; then + exit # BE +fi + new "Kill backend" # Check if premature kill pid=`pgrep -u root -f clixon_backend` diff --git a/test/test_nacm_protocol.sh b/test/test_nacm_protocol.sh index 31e60073..41dfdefb 100755 --- a/test/test_nacm_protocol.sh +++ b/test/test_nacm_protocol.sh @@ -29,8 +29,7 @@ APPNAME=example . ./nacm.sh cfg=$dir/conf_yang.xml -fyang=$dir/test.yang -fyangerr=$dir/err.yang +fyang=$dir/nacm-example.yang cat < $cfg @@ -43,6 +42,7 @@ cat < $cfg /usr/local/lib/$APPNAME/cli $APPNAME /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/lib/$APPNAME/backend /usr/local/var/$APPNAME/$APPNAME.pidfile 1 /usr/local/var/$APPNAME @@ -53,10 +53,13 @@ cat < $cfg EOF cat < $fyang -module $APPNAME{ +module nacm-example{ yang-version 1.1; - namespace "urn:example:clixon"; - prefix ex; + namespace "urn:example:nacm"; + prefix nacm; + import clixon-example { + prefix ex; + } import ietf-netconf-acm { prefix nacm; } @@ -122,7 +125,7 @@ RULES=$(cat < - 0 + 0 EOF ) @@ -163,7 +166,7 @@ expecteq "$(curl -u andy:bar -sS -X PUT -d '{"enable-nacm": true}' http://localh #--------------- nacm enabled new2 "admin get nacm" -expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" '{"example:x": 0} +expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" '{"nacm-example:x": 0} ' # Rule 1: deny-kill-session @@ -188,7 +191,7 @@ new "deny-delete-config: limited fail (restconf) ok" expecteq "$(curl -u wilma:bar -sS -X DELETE http://localhost/restconf/data)" '' new2 "admin get nacm (should be null)" -expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/example:x)" 'null +expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 'null ' new "deny-delete-config: admin ok (restconf)" @@ -206,10 +209,10 @@ expecteq "$(curl -u andy:bar -sS -X PUT -d '{"ietf-netconf-acm:enable-nacm": tru # Rule 3: permit-edit-config new "permit-edit-config: limited ok restconf" -expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"example:x": 2}' http://localhost/restconf/data/example:x)" '' +expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"nacm-example:x": 2}' http://localhost/restconf/data/nacm-example:x)" '' new2 "permit-edit-config: guest fail restconf" -expecteq "$(curl -u guest:bar -sS -X PUT -d '{"example:x": 2}' http://localhost/restconf/data/example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' +expecteq "$(curl -u guest:bar -sS -X PUT -d '{"nacm-example:x": 2}' http://localhost/restconf/data/nacm-example:x)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}} ' new "Kill restconf daemon" sudo pkill -u www-data -f "/www-data/clixon_restconf" From ffecebf32ad5c2883b8d49daf254544a28069f69 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 27 Jan 2019 13:26:15 +0100 Subject: [PATCH 3/7] * NACM Data node READ access module support (RFC8341 3.4.5) * Access control points added for `get` and `get-config` in addition to incoming rpc. * RFC 8341 Example A.2 implemented, see: [test/test_nacm_module.sh] * Added `username` argument on `xmldb_put()` datastore function for NACM data-node write checks * Added `xml_rootchild_node()` lib function as variant of `xml_rootchild()` --- CHANGELOG.md | 10 +- apps/backend/backend_client.c | 87 ++++-- apps/backend/backend_commit.c | 6 +- apps/backend/backend_main.c | 9 +- apps/restconf/restconf_methods.c | 14 +- datastore/datastore_client.c | 2 +- datastore/text/clixon_xmldb_text.c | 63 ++++- datastore/text/clixon_xmldb_text.h | 2 +- example/example_backend.c | 2 +- lib/clixon/clixon_nacm.h | 5 +- lib/clixon/clixon_xml.h | 1 + lib/clixon/clixon_xml_db.h | 4 +- lib/src/clixon_nacm.c | 419 ++++++++++++++++++++++++----- lib/src/clixon_xml.c | 41 ++- lib/src/clixon_xml_db.c | 6 +- lib/src/clixon_xml_map.c | 2 +- test/lib.sh | 5 +- test/test_nacm.sh | 2 +- test/test_nacm_ext.sh | 2 +- test/test_nacm_module.sh | 119 ++++++-- 20 files changed, 656 insertions(+), 145 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 088d8a70..8f5079e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,12 +77,17 @@ * CLICON_YANG_MAIN_FILE Provides a filename with a single module filename. * CLICON_YANG_MAIN_DIR Provides a directory where all yang modules should be loaded. * NACM extension (RFC8341) - * NACM module support (RFC8341 A1+A2) + * NACM Data node READ access module support (RFC8341 3.4.5) + * Access control points added for `get` and `get-config` in addition to incoming rpc. + * RFC 8341 Example A.2 implemented, see: [test/test_nacm_module.sh] + * Remaining work: + * data-node module write/create/delete/update + * data-node path * Recovery user "_nacm_recovery" added. * Example use is restconf PUT when NACM edit-config is permitted, then automatic commit and discard are permitted using recovery user. - * Example user changed adm1 to andy to comply with RFC8341 example ### API changes on existing features (you may need to change your code) +* Added `username` argument on `xmldb_put()` datastore function for NACM data-node write checks * Rearranged yang files * Moved and updated all standard ietf and iana yang files from example and yang/ to `yang/standard`. * Moved clixon yang files from yang to `yang/clixon` @@ -112,6 +117,7 @@ * For backward compatibility, define CLICON_CLI_MODEL_TREENAME_PATCH in clixon_custom.h ### Minor changes +* Added `xml_rootchild_node()` lib function as variant of `xml_rootchild()` * Added -o "