github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/snap/naming/tag.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2020 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 naming
    21  
    22  import (
    23  	"errors"
    24  	"fmt"
    25  	"strings"
    26  )
    27  
    28  var errInvalidSecurityTag = errors.New("invalid security tag")
    29  
    30  // SecurityTag exposes details of a validated snap security tag.
    31  type SecurityTag interface {
    32  	// String returns the entire security tag.
    33  	String() string
    34  
    35  	// InstanceName returns the snap name and instance key.
    36  	InstanceName() string
    37  }
    38  
    39  // AppSecurityTag exposes details of a validated snap application security tag.
    40  type AppSecurityTag interface {
    41  	SecurityTag
    42  	// AppName returns the name of the application.
    43  	AppName() string
    44  }
    45  
    46  type appSecurityTag struct {
    47  	instanceName string
    48  	appName      string
    49  }
    50  
    51  func (t appSecurityTag) String() string {
    52  	return fmt.Sprintf("snap.%s.%s", t.instanceName, t.appName)
    53  }
    54  
    55  func (t appSecurityTag) InstanceName() string {
    56  	return t.instanceName
    57  }
    58  
    59  func (t appSecurityTag) AppName() string {
    60  	return t.appName
    61  }
    62  
    63  // HookSecurityTag exposes details of a validated snap hook security tag.
    64  type HookSecurityTag interface {
    65  	SecurityTag
    66  	// HookName returns the name of the hook.
    67  	HookName() string
    68  }
    69  
    70  type hookSecurityTag struct {
    71  	instanceName string
    72  	hookName     string
    73  }
    74  
    75  func (t hookSecurityTag) String() string {
    76  	return fmt.Sprintf("snap.%s.hook.%s", t.instanceName, t.hookName)
    77  }
    78  
    79  func (t hookSecurityTag) InstanceName() string {
    80  	return t.instanceName
    81  }
    82  
    83  func (t hookSecurityTag) HookName() string {
    84  	return t.hookName
    85  }
    86  
    87  // ParseSecurityTag parses a snap security tag and returns a parsed representation or an error.
    88  //
    89  // Further type assertions can be used to described the particular form, either
    90  // describing an application or a hook specific security tag.
    91  func ParseSecurityTag(tag string) (SecurityTag, error) {
    92  	// We expect at most four parts. Split with up to five parts so that the
    93  	// len(parts) test catches invalid format tags very early.
    94  	parts := strings.SplitN(tag, ".", 5)
    95  	// We expect either three or four components.
    96  	if len(parts) != 3 && len(parts) != 4 {
    97  		return nil, errInvalidSecurityTag
    98  	}
    99  	// We expect "snap" and the snap instance name as first two fields.
   100  	snapLiteral, snapName := parts[0], parts[1]
   101  	if snapLiteral != "snap" {
   102  		return nil, errInvalidSecurityTag
   103  	}
   104  	if err := ValidateInstance(snapName); err != nil {
   105  		return nil, errInvalidSecurityTag
   106  	}
   107  	// Depending on the type of the tag we either expect application name or
   108  	// the "hook" literal and the hook name.
   109  	if len(parts) == 3 {
   110  		appName := parts[2]
   111  		if err := ValidateApp(appName); err != nil {
   112  			return nil, errInvalidSecurityTag
   113  		}
   114  		return &appSecurityTag{instanceName: snapName, appName: appName}, nil
   115  	} else {
   116  		hookLiteral, hookName := parts[2], parts[3]
   117  		if hookLiteral != "hook" {
   118  			return nil, errInvalidSecurityTag
   119  		}
   120  		if err := ValidateHook(hookName); err != nil {
   121  			return nil, errInvalidSecurityTag
   122  		}
   123  		return &hookSecurityTag{instanceName: snapName, hookName: hookName}, nil
   124  	}
   125  }
   126  
   127  // ParseAppSecurityTag parses an app security tag.
   128  func ParseAppSecurityTag(tag string) (AppSecurityTag, error) {
   129  	parsedTag, err := ParseSecurityTag(tag)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	if parsedAppTag, ok := parsedTag.(AppSecurityTag); ok {
   134  		return parsedAppTag, nil
   135  	}
   136  	return nil, fmt.Errorf("%q is not an app security tag", tag)
   137  }
   138  
   139  // ParseHookSecurityTag parses a hook security tag.
   140  func ParseHookSecurityTag(tag string) (HookSecurityTag, error) {
   141  	parsedTag, err := ParseSecurityTag(tag)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  	if parsedHookTag, ok := parsedTag.(HookSecurityTag); ok {
   146  		return parsedHookTag, nil
   147  	}
   148  	return nil, fmt.Errorf("%q is not a hook security tag", tag)
   149  }