gitee.com/mysnapcore/mysnapd@v0.1.0/interfaces/policy/helpers.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2017 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 policy
    21  
    22  import (
    23  	"fmt"
    24  	"strings"
    25  
    26  	"gitee.com/mysnapcore/mysnapd/asserts"
    27  	"gitee.com/mysnapcore/mysnapd/release"
    28  	"gitee.com/mysnapcore/mysnapd/snap"
    29  )
    30  
    31  // check helpers
    32  
    33  func checkSnapType(snapInfo *snap.Info, types []string) error {
    34  	if len(types) == 0 {
    35  		return nil
    36  	}
    37  	snapType := snapInfo.Type()
    38  	s := string(snapType)
    39  	if snapType == snap.TypeOS || snapType == snap.TypeSnapd {
    40  		// we use "core" in the assertions and we need also to
    41  		// allow for the "snapd" snap
    42  		s = "core"
    43  	}
    44  	for _, t := range types {
    45  		if t == s {
    46  			return nil
    47  		}
    48  	}
    49  	return fmt.Errorf("snap type does not match")
    50  }
    51  
    52  func checkID(kind, id string, ids []string, special map[string]string) error {
    53  	if len(ids) == 0 {
    54  		return nil
    55  	}
    56  	if id == "" { // unset values never match
    57  		return fmt.Errorf("%s does not match", kind)
    58  	}
    59  	for _, cand := range ids {
    60  		if strings.HasPrefix(cand, "$") {
    61  			cand = special[cand]
    62  			if cand == "" { // we ignore unknown special "ids"
    63  				continue
    64  			}
    65  		}
    66  		if id == cand {
    67  			return nil
    68  		}
    69  	}
    70  	return fmt.Errorf("%s does not match", kind)
    71  }
    72  
    73  func checkOnClassic(c *asserts.OnClassicConstraint) error {
    74  	if c == nil {
    75  		return nil
    76  	}
    77  	if c.Classic != release.OnClassic {
    78  		return fmt.Errorf("on-classic mismatch")
    79  	}
    80  	if c.Classic && len(c.SystemIDs) != 0 {
    81  		return checkID("operating system ID", release.ReleaseInfo.ID, c.SystemIDs, nil)
    82  	}
    83  	return nil
    84  }
    85  
    86  func checkDeviceScope(c *asserts.DeviceScopeConstraint, model *asserts.Model, store *asserts.Store) error {
    87  	if c == nil {
    88  		return nil
    89  	}
    90  	opts := asserts.DeviceScopeConstraintCheckOptions{
    91  		UseFriendlyStores: true,
    92  	}
    93  	return c.Check(model, store, &opts)
    94  }
    95  
    96  func checkNameConstraints(c *asserts.NameConstraints, iface, which, name string) error {
    97  	if c == nil {
    98  		return nil
    99  	}
   100  	special := map[string]string{
   101  		"$INTERFACE": iface,
   102  	}
   103  	return c.Check(which, name, special)
   104  }
   105  
   106  func checkPlugConnectionConstraints1(connc *ConnectCandidate, constraints *asserts.PlugConnectionConstraints) error {
   107  	if err := checkNameConstraints(constraints.PlugNames, connc.Plug.Interface(), "plug name", connc.Plug.Name()); err != nil {
   108  		return err
   109  	}
   110  	if err := checkNameConstraints(constraints.SlotNames, connc.Slot.Interface(), "slot name", connc.Slot.Name()); err != nil {
   111  		return err
   112  	}
   113  
   114  	if err := constraints.PlugAttributes.Check(connc.Plug, connc); err != nil {
   115  		return err
   116  	}
   117  	if err := constraints.SlotAttributes.Check(connc.Slot, connc); err != nil {
   118  		return err
   119  	}
   120  	if err := checkSnapType(connc.Slot.Snap(), constraints.SlotSnapTypes); err != nil {
   121  		return err
   122  	}
   123  	if err := checkID("snap id", connc.slotSnapID(), constraints.SlotSnapIDs, nil); err != nil {
   124  		return err
   125  	}
   126  	err := checkID("publisher id", connc.slotPublisherID(), constraints.SlotPublisherIDs, map[string]string{
   127  		"$PLUG_PUBLISHER_ID": connc.plugPublisherID(),
   128  	})
   129  	if err != nil {
   130  		return err
   131  	}
   132  	if err := checkOnClassic(constraints.OnClassic); err != nil {
   133  		return err
   134  	}
   135  	if err := checkDeviceScope(constraints.DeviceScope, connc.Model, connc.Store); err != nil {
   136  		return err
   137  	}
   138  	return nil
   139  }
   140  
   141  func checkPlugConnectionAltConstraints(connc *ConnectCandidate, altConstraints []*asserts.PlugConnectionConstraints) (*asserts.PlugConnectionConstraints, error) {
   142  	var firstErr error
   143  	// OR of constraints
   144  	for _, constraints := range altConstraints {
   145  		err := checkPlugConnectionConstraints1(connc, constraints)
   146  		if err == nil {
   147  			return constraints, nil
   148  		}
   149  		if firstErr == nil {
   150  			firstErr = err
   151  		}
   152  	}
   153  	return nil, firstErr
   154  }
   155  
   156  func checkSlotConnectionConstraints1(connc *ConnectCandidate, constraints *asserts.SlotConnectionConstraints) error {
   157  	if err := checkNameConstraints(constraints.PlugNames, connc.Plug.Interface(), "plug name", connc.Plug.Name()); err != nil {
   158  		return err
   159  	}
   160  	if err := checkNameConstraints(constraints.SlotNames, connc.Slot.Interface(), "slot name", connc.Slot.Name()); err != nil {
   161  		return err
   162  	}
   163  
   164  	if err := constraints.PlugAttributes.Check(connc.Plug, connc); err != nil {
   165  		return err
   166  	}
   167  	if err := constraints.SlotAttributes.Check(connc.Slot, connc); err != nil {
   168  		return err
   169  	}
   170  	if err := checkSnapType(connc.Plug.Snap(), constraints.PlugSnapTypes); err != nil {
   171  		return err
   172  	}
   173  	if err := checkID("snap id", connc.plugSnapID(), constraints.PlugSnapIDs, nil); err != nil {
   174  		return err
   175  	}
   176  	err := checkID("publisher id", connc.plugPublisherID(), constraints.PlugPublisherIDs, map[string]string{
   177  		"$SLOT_PUBLISHER_ID": connc.slotPublisherID(),
   178  	})
   179  	if err != nil {
   180  		return err
   181  	}
   182  	if err := checkOnClassic(constraints.OnClassic); err != nil {
   183  		return err
   184  	}
   185  	if err := checkDeviceScope(constraints.DeviceScope, connc.Model, connc.Store); err != nil {
   186  		return err
   187  	}
   188  	return nil
   189  }
   190  
   191  func checkSlotConnectionAltConstraints(connc *ConnectCandidate, altConstraints []*asserts.SlotConnectionConstraints) (*asserts.SlotConnectionConstraints, error) {
   192  	var firstErr error
   193  	// OR of constraints
   194  	for _, constraints := range altConstraints {
   195  		err := checkSlotConnectionConstraints1(connc, constraints)
   196  		if err == nil {
   197  			return constraints, nil
   198  		}
   199  		if firstErr == nil {
   200  			firstErr = err
   201  		}
   202  	}
   203  	return nil, firstErr
   204  }
   205  
   206  func checkSnapTypeSlotInstallationConstraints1(slot *snap.SlotInfo, constraints *asserts.SlotInstallationConstraints) error {
   207  	if err := checkSnapType(slot.Snap, constraints.SlotSnapTypes); err != nil {
   208  		return err
   209  	}
   210  	if err := checkOnClassic(constraints.OnClassic); err != nil {
   211  		return err
   212  	}
   213  	return nil
   214  }
   215  
   216  func checkMinimalSlotInstallationAltConstraints(slot *snap.SlotInfo, altConstraints []*asserts.SlotInstallationConstraints) (bool, error) {
   217  	var firstErr error
   218  	var hasSnapTypeConstraints bool
   219  	// OR of constraints
   220  	for _, constraints := range altConstraints {
   221  		if constraints.OnClassic == nil && len(constraints.SlotSnapTypes) == 0 {
   222  			continue
   223  		}
   224  		hasSnapTypeConstraints = true
   225  		err := checkSnapTypeSlotInstallationConstraints1(slot, constraints)
   226  		if err == nil {
   227  			return true, nil
   228  		}
   229  		if firstErr == nil {
   230  			firstErr = err
   231  		}
   232  	}
   233  	return hasSnapTypeConstraints, firstErr
   234  }
   235  
   236  func checkSlotInstallationConstraints1(ic *InstallCandidate, slot *snap.SlotInfo, constraints *asserts.SlotInstallationConstraints) error {
   237  	if err := checkNameConstraints(constraints.SlotNames, slot.Interface, "slot name", slot.Name); err != nil {
   238  		return err
   239  	}
   240  
   241  	// TODO: allow evaluated attr constraints here too?
   242  	if err := constraints.SlotAttributes.Check(slot, nil); err != nil {
   243  		return err
   244  	}
   245  	if err := checkSnapType(slot.Snap, constraints.SlotSnapTypes); err != nil {
   246  		return err
   247  	}
   248  	if err := checkID("snap id", ic.snapID(), constraints.SlotSnapIDs, nil); err != nil {
   249  		return err
   250  	}
   251  	if err := checkOnClassic(constraints.OnClassic); err != nil {
   252  		return err
   253  	}
   254  	if err := checkDeviceScope(constraints.DeviceScope, ic.Model, ic.Store); err != nil {
   255  		return err
   256  	}
   257  	return nil
   258  }
   259  
   260  func checkSlotInstallationAltConstraints(ic *InstallCandidate, slot *snap.SlotInfo, altConstraints []*asserts.SlotInstallationConstraints) error {
   261  	var firstErr error
   262  	// OR of constraints
   263  	for _, constraints := range altConstraints {
   264  		err := checkSlotInstallationConstraints1(ic, slot, constraints)
   265  		if err == nil {
   266  			return nil
   267  		}
   268  		if firstErr == nil {
   269  			firstErr = err
   270  		}
   271  	}
   272  	return firstErr
   273  }
   274  
   275  func checkPlugInstallationConstraints1(ic *InstallCandidate, plug *snap.PlugInfo, constraints *asserts.PlugInstallationConstraints) error {
   276  	if err := checkNameConstraints(constraints.PlugNames, plug.Interface, "plug name", plug.Name); err != nil {
   277  		return err
   278  	}
   279  
   280  	// TODO: allow evaluated attr constraints here too?
   281  	if err := constraints.PlugAttributes.Check(plug, nil); err != nil {
   282  		return err
   283  	}
   284  	if err := checkSnapType(plug.Snap, constraints.PlugSnapTypes); err != nil {
   285  		return err
   286  	}
   287  	if err := checkID("snap id", ic.snapID(), constraints.PlugSnapIDs, nil); err != nil {
   288  		return err
   289  	}
   290  	if err := checkOnClassic(constraints.OnClassic); err != nil {
   291  		return err
   292  	}
   293  	if err := checkDeviceScope(constraints.DeviceScope, ic.Model, ic.Store); err != nil {
   294  		return err
   295  	}
   296  	return nil
   297  }
   298  
   299  func checkPlugInstallationAltConstraints(ic *InstallCandidate, plug *snap.PlugInfo, altConstraints []*asserts.PlugInstallationConstraints) error {
   300  	var firstErr error
   301  	// OR of constraints
   302  	for _, constraints := range altConstraints {
   303  		err := checkPlugInstallationConstraints1(ic, plug, constraints)
   304  		if err == nil {
   305  			return nil
   306  		}
   307  		if firstErr == nil {
   308  			firstErr = err
   309  		}
   310  	}
   311  	return firstErr
   312  }
   313  
   314  // sideArity carries relevant arity constraints for successful
   315  // allow-auto-connection rules. It implements policy.SideArity.
   316  // ATM only slots-per-plug might have an interesting non-default
   317  // value.
   318  // See: https://forum.snapcraft.io/t/plug-slot-declaration-rules-greedy-plugs/12438
   319  type sideArity struct {
   320  	slotsPerPlug asserts.SideArityConstraint
   321  }
   322  
   323  func (a sideArity) SlotsPerPlugOne() bool {
   324  	return a.slotsPerPlug.N == 1
   325  }
   326  
   327  func (a sideArity) SlotsPerPlugAny() bool {
   328  	return a.slotsPerPlug.Any()
   329  }