github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/arn/arn.go (about)

     1  // Copyright (c) 2015-2023 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     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 Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package arn
    19  
    20  import (
    21  	"fmt"
    22  	"regexp"
    23  	"strings"
    24  )
    25  
    26  // ARN structure:
    27  //
    28  // arn:partition:service:region:account-id:resource-type/resource-id
    29  //
    30  // In this implementation, account-id is empty.
    31  //
    32  // Reference: https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html
    33  
    34  type arnPartition string
    35  
    36  const (
    37  	arnPartitionMinio arnPartition = "minio"
    38  )
    39  
    40  type arnService string
    41  
    42  const (
    43  	arnServiceIAM arnService = "iam"
    44  )
    45  
    46  type arnResourceType string
    47  
    48  const (
    49  	arnResourceTypeRole arnResourceType = "role"
    50  )
    51  
    52  // ARN - representation of resources based on AWS ARNs.
    53  type ARN struct {
    54  	Partition    arnPartition
    55  	Service      arnService
    56  	Region       string
    57  	ResourceType arnResourceType
    58  	ResourceID   string
    59  }
    60  
    61  // Allows english letters, numbers, '.', '-', '_' and '/'. Starts with a
    62  // letter or digit. At least 1 character long.
    63  var validResourceIDRegex = regexp.MustCompile(`[A-Za-z0-9_/\.-]+$`)
    64  
    65  // NewIAMRoleARN - returns an ARN for a role in MinIO.
    66  func NewIAMRoleARN(resourceID, serverRegion string) (ARN, error) {
    67  	if !validResourceIDRegex.MatchString(resourceID) {
    68  		return ARN{}, fmt.Errorf("Invalid resource ID: %s", resourceID)
    69  	}
    70  	return ARN{
    71  		Partition:    arnPartitionMinio,
    72  		Service:      arnServiceIAM,
    73  		Region:       serverRegion,
    74  		ResourceType: arnResourceTypeRole,
    75  		ResourceID:   resourceID,
    76  	}, nil
    77  }
    78  
    79  // String - returns string representation of the ARN.
    80  func (arn ARN) String() string {
    81  	return strings.Join(
    82  		[]string{
    83  			"arn",
    84  			string(arn.Partition),
    85  			string(arn.Service),
    86  			arn.Region,
    87  			"", // account-id is always empty in this implementation
    88  			string(arn.ResourceType) + "/" + arn.ResourceID,
    89  		},
    90  		":",
    91  	)
    92  }
    93  
    94  // Parse - parses an ARN string into a type.
    95  func Parse(arnStr string) (arn ARN, err error) {
    96  	ps := strings.Split(arnStr, ":")
    97  	if len(ps) != 6 ||
    98  		ps[0] != "arn" {
    99  		err = fmt.Errorf("Invalid ARN string format")
   100  		return
   101  	}
   102  
   103  	if ps[1] != string(arnPartitionMinio) {
   104  		err = fmt.Errorf("Invalid ARN - bad partition field")
   105  		return
   106  	}
   107  
   108  	if ps[2] != string(arnServiceIAM) {
   109  		err = fmt.Errorf("Invalid ARN - bad service field")
   110  		return
   111  	}
   112  
   113  	// ps[3] is region and is not validated here. If the region is invalid,
   114  	// the ARN would not match any configured ARNs in the server.
   115  
   116  	if ps[4] != "" {
   117  		err = fmt.Errorf("Invalid ARN - unsupported account-id field")
   118  		return
   119  	}
   120  
   121  	res := strings.SplitN(ps[5], "/", 2)
   122  	if len(res) != 2 {
   123  		err = fmt.Errorf("Invalid ARN - resource does not contain a \"/\"")
   124  		return
   125  	}
   126  
   127  	if res[0] != string(arnResourceTypeRole) {
   128  		err = fmt.Errorf("Invalid ARN: resource type is invalid.")
   129  		return
   130  	}
   131  
   132  	if !validResourceIDRegex.MatchString(res[1]) {
   133  		err = fmt.Errorf("Invalid resource ID: %s", res[1])
   134  		return
   135  	}
   136  
   137  	arn = ARN{
   138  		Partition:    arnPartitionMinio,
   139  		Service:      arnServiceIAM,
   140  		Region:       ps[3],
   141  		ResourceType: arnResourceTypeRole,
   142  		ResourceID:   res[1],
   143  	}
   144  	return
   145  }