gitee.com/mysnapcore/mysnapd@v0.1.0/polkit/validate/validate_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2021 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package validate_test
    21  
    22  import (
    23  	"bytes"
    24  	"fmt"
    25  	"sort"
    26  	"testing"
    27  
    28  	. "gopkg.in/check.v1"
    29  
    30  	"gitee.com/mysnapcore/mysnapd/polkit/validate"
    31  )
    32  
    33  type validateSuite struct{}
    34  
    35  var _ = Suite(&validateSuite{})
    36  
    37  func Test(t *testing.T) {
    38  	TestingT(t)
    39  }
    40  
    41  func validateString(xml string) ([]string, error) {
    42  	return validate.ValidatePolicy(bytes.NewBufferString(xml))
    43  }
    44  
    45  func (s *validateSuite) TestRootElement(c *C) {
    46  	// Extra elements after root
    47  	_, err := validateString("<policyconfig/><policyconfig/>")
    48  	c.Check(err, ErrorMatches, `invalid XML: additional data after root element`)
    49  
    50  	// Extra incomplete elements after root element
    51  	_, err = validateString("<policyconfig/><incomplete>")
    52  	c.Check(err, ErrorMatches, `invalid XML: additional data after root element`)
    53  
    54  	// Wrong root element
    55  	_, err = validateString("<xyz/>")
    56  	c.Check(err, ErrorMatches, `expected element type <policyconfig> but have <xyz>`)
    57  
    58  	// Wrong namespace for root element
    59  	_, err = validateString(`<policyconfig xmlns="http://example.org/ns"/>`)
    60  	c.Check(err, ErrorMatches, `root element must be <policyconfig>`)
    61  
    62  	// Invalid XML
    63  	_, err = validateString("<policyconfig>incomplete")
    64  	c.Check(err, ErrorMatches, `XML syntax error on line .*`)
    65  }
    66  
    67  func (s *validateSuite) TestPolicyConfigElement(c *C) {
    68  	_, err := validateString("<policyconfig/>")
    69  	c.Check(err, IsNil)
    70  
    71  	// Extra attributes are not allowed
    72  	_, err = validateString(`<policyconfig foo="bar"/>`)
    73  	c.Check(err, ErrorMatches, `<policyconfig> element contains unexpected attributes`)
    74  
    75  	// Unexpected child elements
    76  	_, err = validateString("<policyconfig><xyz/></policyconfig>")
    77  	c.Check(err, ErrorMatches, `<policyconfig> element contains unexpected children`)
    78  
    79  	// Unexpected character data
    80  	_, err = validateString("<policyconfig>xyz</policyconfig>")
    81  	c.Check(err, ErrorMatches, `<policyconfig> element contains unexpected character data`)
    82  
    83  	// Supports <vendor>, <vendor_url>, and <icon_name> parameters
    84  	_, err = validateString(`<policyconfig>
    85    <vendor>vendor</vendor>
    86    <vendor_url>url</vendor_url>
    87    <icon_name>icon</icon_name>
    88  </policyconfig>`)
    89  	c.Check(err, IsNil)
    90  
    91  	// Duplicates of those elements are not allowed
    92  	_, err = validateString(`<policyconfig>
    93    <vendor>vendor</vendor>
    94    <vendor>vendor</vendor>
    95  </policyconfig>`)
    96  	c.Check(err, ErrorMatches, `multiple <vendor> elements found under <policyconfig>`)
    97  
    98  	_, err = validateString(`<policyconfig>
    99    <vendor_url>url</vendor_url>
   100    <vendor_url>url</vendor_url>
   101  </policyconfig>`)
   102  	c.Check(err, ErrorMatches, `multiple <vendor_url> elements found under <policyconfig>`)
   103  
   104  	_, err = validateString(`<policyconfig>
   105    <icon_name>icon</icon_name>
   106    <icon_name>icon</icon_name>
   107  </policyconfig>`)
   108  	c.Check(err, ErrorMatches, `multiple <icon_name> elements found under <policyconfig>`)
   109  }
   110  
   111  func validateAction(xml string) ([]string, error) {
   112  	return validateString("<policyconfig>" + xml + "</policyconfig>")
   113  }
   114  
   115  func (s *validateSuite) TestActionElement(c *C) {
   116  	// The ID of an action is extracted on successful validation
   117  	actionIDs, err := validateAction(`<action id="foo">
   118    <description>desc</description>
   119    <message>msg</message>
   120  </action>`)
   121  	c.Check(err, IsNil)
   122  	c.Check(actionIDs, DeepEquals, []string{"foo"})
   123  
   124  	// Actions must have an ID
   125  	_, err = validateAction("<action/>")
   126  	c.Check(err, ErrorMatches, `<action> elements must have an ID`)
   127  
   128  	// Other attributes are not allowed
   129  	_, err = validateAction(`<action bar="foo"/>`)
   130  	c.Check(err, ErrorMatches, `<action> element contains unexpected attributes`)
   131  
   132  	// Unexpected child elements are not allowed
   133  	_, err = validateAction(`<action id="foo"><xyz/></action>`)
   134  	c.Check(err, ErrorMatches, `<action> element contains unexpected children`)
   135  
   136  	// Character data not allowed inside element
   137  	_, err = validateAction(`<action id="foo">xyz</action>`)
   138  	c.Check(err, ErrorMatches, `<action> element contains unexpected character data`)
   139  
   140  	// Action elements can also contain <vendor>, <vendor_url>,
   141  	// and <icon_name> elements.
   142  	_, err = validateAction(`<action id="foo">
   143    <description>desc</description><message>msg</message>
   144    <vendor>vendor</vendor>
   145    <vendor_url>url</vendor_url>
   146    <icon_name>icon</icon_name>
   147  </action>`)
   148  	c.Check(err, IsNil)
   149  
   150  	// Empty versions of those elements are not allowed
   151  	_, err = validateAction(`<action id="foo">
   152    <description>desc</description><message>msg</message>
   153    <vendor/>
   154  </action>`)
   155  	c.Check(err, ErrorMatches, `<vendor> element has no character data`)
   156  
   157  	// Duplicates of those elements are not allowed
   158  	_, err = validateAction(`<action id="foo">
   159    <description>desc</description><message>msg</message>
   160    <vendor>vendor</vendor>
   161    <vendor>vendor</vendor>
   162  </action>`)
   163  	c.Check(err, ErrorMatches, `multiple <vendor> elements found under <action>`)
   164  
   165  	_, err = validateAction(`<action id="foo">
   166    <description>desc</description><message>msg</message>
   167    <vendor_url>url</vendor_url>
   168    <vendor_url>url</vendor_url>
   169  </action>`)
   170  	c.Check(err, ErrorMatches, `multiple <vendor_url> elements found under <action>`)
   171  
   172  	_, err = validateAction(`<action id="foo">
   173    <description>desc</description><message>msg</message>
   174    <icon_name>icon</icon_name>
   175    <icon_name>icon</icon_name>
   176  </action>`)
   177  	c.Check(err, ErrorMatches, `multiple <icon_name> elements found under <action>`)
   178  
   179  	// The <description> and <message> elements accept
   180  	// gettext-domain and xml:lang attributes
   181  	_, err = validateAction(`<action id="foo">
   182    <description gettext-domain="bar" xml:lang="en-GB">desc</description>
   183    <message gettext-domain="bar" xml:lang="en-GB">desc</message>
   184  </action>`)
   185  	c.Check(err, IsNil)
   186  
   187  	// Other attributes or child elements on <description> and
   188  	// <message> are forbidden
   189  	_, err = validateAction(`<action id="foo">
   190    <description bar="foo">desc</description>
   191    <message>msg</message>
   192  </action>`)
   193  	c.Check(err, ErrorMatches, `<description> element contains unexpected attributes`)
   194  
   195  	_, err = validateAction(`<action id="foo">
   196    <description>desc<xyz/></description>
   197    <message>msg</message>
   198  </action>`)
   199  	c.Check(err, ErrorMatches, `<description> element contains unexpected children`)
   200  
   201  	_, err = validateAction(`<action id="foo">
   202    <description>desc</description>
   203    <message bar="foo">msg</message>
   204  </action>`)
   205  	c.Check(err, ErrorMatches, `<message> element contains unexpected attributes`)
   206  
   207  	_, err = validateAction(`<action id="foo">
   208    <description>desc</description>
   209    <message>msg<xyz/></message>
   210  </action>`)
   211  	c.Check(err, ErrorMatches, `<message> element contains unexpected children`)
   212  
   213  	// Multiple <description> and <message> children are allowed
   214  	// children
   215  	_, err = validateAction(`<action id="foo">
   216    <description>desc</description>
   217    <description>desc</description>
   218    <description>desc</description>
   219    <message>msg</message>
   220  </action>`)
   221  	c.Check(err, IsNil)
   222  
   223  	_, err = validateAction(`<action id="foo">
   224    <description>desc</description>
   225    <message>msg</message>
   226    <message>msg</message>
   227    <message>msg</message>
   228  </action>`)
   229  	c.Check(err, IsNil)
   230  
   231  	// But at least one is required
   232  	_, err = validateAction(`<action id="foo">
   233    <message>msg</message>
   234  </action>`)
   235  	c.Check(err, ErrorMatches, `<action> element missing <description> child`)
   236  
   237  	_, err = validateAction(`<action id="foo">
   238    <description>desc</description>
   239  </action>`)
   240  	c.Check(err, ErrorMatches, `<action> element missing <message> child`)
   241  }
   242  
   243  func validateActionDefaults(xml string) error {
   244  	_, err := validateAction(fmt.Sprintf(`<action id="foo">
   245    <description>desc</description><message>msg</message>
   246    %s
   247  </action>`, xml))
   248  	return err
   249  }
   250  
   251  func (s *validateSuite) TestDefaultsElement(c *C) {
   252  	// Actions can have a single <defaults> element
   253  	err := validateActionDefaults(`<defaults/>`)
   254  	c.Check(err, IsNil)
   255  
   256  	err = validateActionDefaults(`<defaults/><defaults/>`)
   257  	c.Check(err, ErrorMatches, `<action> element has multiple <defaults> children`)
   258  
   259  	// The <defaults> element does not accept attributes, unknown children or character data
   260  	err = validateActionDefaults(`<defaults foo="bar"/>`)
   261  	c.Check(err, ErrorMatches, `<defaults> element contains unexpected attributes`)
   262  
   263  	err = validateActionDefaults(`<defaults>xyz</defaults>`)
   264  	c.Check(err, ErrorMatches, `<defaults> element contains unexpected character data`)
   265  
   266  	err = validateActionDefaults(`<defaults><xyz/></defaults>`)
   267  	c.Check(err, ErrorMatches, `<defaults> element contains unexpected children`)
   268  
   269  	// The defaults section contains default access rules for the action
   270  	err = validateActionDefaults(`<defaults>
   271    <allow_any>yes</allow_any>
   272    <allow_inactive>yes</allow_inactive>
   273    <allow_active>yes</allow_active>
   274  </defaults>`)
   275  	c.Check(err, IsNil)
   276  
   277  	for _, mode := range []string{"allow_any", "allow_inactive", "allow_active"} {
   278  		// Only one instance of the element is allowed
   279  		err = validateActionDefaults(fmt.Sprintf(`<defaults>
   280    <%s>yes</%s>
   281    <%s>yes</%s>
   282  </defaults>`, mode, mode, mode, mode))
   283  		c.Check(err, ErrorMatches, fmt.Sprintf("multiple <%s> elements found under <defaults>", mode))
   284  
   285  		// No attributes or child elements allowed
   286  		err = validateActionDefaults(fmt.Sprintf(`<defaults>
   287    <%s foo="bar">yes</%s>
   288  </defaults>`, mode, mode))
   289  		c.Check(err, ErrorMatches, fmt.Sprintf("<%s> element contains unexpected attributes", mode))
   290  
   291  		err = validateActionDefaults(fmt.Sprintf(`<defaults>
   292    <%s>yes<xyz/></%s>
   293  </defaults>`, mode, mode))
   294  		c.Check(err, ErrorMatches, fmt.Sprintf("<%s> element contains unexpected children", mode))
   295  
   296  		// Unknown or missing values are rejected
   297  		err = validateActionDefaults(fmt.Sprintf(`<defaults>
   298    <%s>unknown</%s>
   299  </defaults>`, mode, mode))
   300  		c.Check(err, ErrorMatches, fmt.Sprintf(`invalid value for <%s>: "unknown"`, mode))
   301  
   302  		err = validateActionDefaults(fmt.Sprintf(`<defaults>
   303    <%s/>
   304  </defaults>`, mode))
   305  		c.Check(err, ErrorMatches, fmt.Sprintf(`invalid value for <%s>: ""`, mode))
   306  
   307  		// Known values are accepted:
   308  		for _, value := range []string{"no", "yes", "auth_self", "auth_admin", "auth_self_keep", "auth_admin_keep"} {
   309  			err = validateActionDefaults(fmt.Sprintf(`<defaults>
   310    <%s>%s</%s>
   311  </defaults>`, mode, value, mode))
   312  			c.Check(err, IsNil)
   313  		}
   314  	}
   315  }
   316  
   317  func validateAnnotation(xml string) ([]string, error) {
   318  	return validateAction(fmt.Sprintf(`<action id="action_id">
   319    <description>desc</description><message>msg</message>
   320    %s
   321  </action>`, xml))
   322  }
   323  
   324  func (s *validateSuite) TestAnnotateElement(c *C) {
   325  	actionIDs, err := validateAnnotation(`<annotate key="org.freedesktop.policykit.imply">implied_id</annotate>`)
   326  	c.Check(err, IsNil)
   327  	sort.Strings(actionIDs)
   328  	c.Check(actionIDs, DeepEquals, []string{"action_id", "implied_id"})
   329  
   330  	// <annotate> elements do not accept unknown attributes or
   331  	// child elements
   332  	_, err = validateAnnotation(`<annotate foo="bar"/>`)
   333  	c.Check(err, ErrorMatches, `<annotate> element contains unexpected attributes`)
   334  	_, err = validateAnnotation(`<annotate><xyz/></annotate>`)
   335  	c.Check(err, ErrorMatches, `<annotate> element contains unexpected children`)
   336  
   337  	// The key parameter is required
   338  	_, err = validateAnnotation(`<annotate/>`)
   339  	c.Check(err, ErrorMatches, `<annotate> elements must have a key attribute`)
   340  
   341  	// At present, only "imply" annotations are accepted
   342  	_, err = validateAnnotation(`<annotate key="xyz">foo</annotate>`)
   343  	c.Check(err, ErrorMatches, `unsupported annotation "xyz"`)
   344  
   345  	// "imply" annotations take a whitespace separated list of
   346  	// action IDs that are returned by the validation function
   347  	actionIDs, err = validateAnnotation(`<annotate key="org.freedesktop.policykit.imply">id1 id2 id3 id3</annotate>`)
   348  	c.Check(err, IsNil)
   349  	sort.Strings(actionIDs)
   350  	c.Check(actionIDs, DeepEquals, []string{"action_id", "id1", "id2", "id3"})
   351  
   352  	// Annotation elements must not be empty
   353  	_, err = validateAnnotation(`<annotate key="org.freedesktop.policykit.imply"/>`)
   354  	c.Check(err, ErrorMatches, `<annotate> elements must contain character data`)
   355  
   356  	// Multiple <annotate> elements are accepted
   357  	actionIDs, err = validateAnnotation(`
   358  <annotate key="org.freedesktop.policykit.imply">id1</annotate>
   359  <annotate key="org.freedesktop.policykit.imply">id2</annotate>`)
   360  	c.Check(err, IsNil)
   361  	sort.Strings(actionIDs)
   362  	c.Check(actionIDs, DeepEquals, []string{"action_id", "id1", "id2"})
   363  }
   364  
   365  func (s *validateSuite) TestActionIDExtraction(c *C) {
   366  	actionIDs, err := validateString(`<policyconfig>
   367    <!-- a comment -->
   368    <action id="action1">
   369      <description>desc1</description>
   370      <message>msg1</message>
   371    </action>
   372    <action id="action2">
   373      <description>desc1</description>
   374      <message>msg1</message>
   375      <annotate key="org.freedesktop.policykit.imply">action3</annotate>
   376    </action>
   377    <action id="action3">
   378      <description>desc1</description>
   379      <message>msg1</message>
   380      <annotate key="org.freedesktop.policykit.imply">action2 action4</annotate>
   381    </action>
   382  </policyconfig>`)
   383  	c.Check(err, IsNil)
   384  	sort.Strings(actionIDs)
   385  	c.Check(actionIDs, DeepEquals, []string{"action1", "action2", "action3", "action4"})
   386  }