github.com/cs3org/reva/v2@v2.27.7/pkg/storage/registry/static/static.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package static
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"fmt"
    25  	"path"
    26  	"regexp"
    27  	"strings"
    28  
    29  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    30  	registrypb "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1"
    31  	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
    32  	"github.com/cs3org/reva/v2/pkg/errtypes"
    33  	"github.com/cs3org/reva/v2/pkg/sharedconf"
    34  	"github.com/cs3org/reva/v2/pkg/storage"
    35  	"github.com/cs3org/reva/v2/pkg/storage/registry/registry"
    36  	"github.com/cs3org/reva/v2/pkg/storage/utils/templates"
    37  	"github.com/mitchellh/mapstructure"
    38  )
    39  
    40  func init() {
    41  	registry.Register("static", New)
    42  }
    43  
    44  var bracketRegex = regexp.MustCompile(`\[(.*?)\]`)
    45  
    46  type alias struct {
    47  	Address string `mapstructure:"address"`
    48  	ID      string `mapstructure:"provider_id"`
    49  }
    50  type rule struct {
    51  	Mapping           string           `mapstructure:"mapping"`
    52  	Address           string           `mapstructure:"address"`
    53  	ProviderID        string           `mapstructure:"provider_id"`
    54  	ProviderPath      string           `mapstructure:"provider_path"`
    55  	Aliases           map[string]alias `mapstructure:"aliases"`
    56  	AllowedUserAgents []string         `mapstructure:"allowed_user_agents"`
    57  }
    58  
    59  type config struct {
    60  	Rules        map[string]rule `mapstructure:"rules"`
    61  	HomeProvider string          `mapstructure:"home_provider"`
    62  }
    63  
    64  func (c *config) init() {
    65  	if c.HomeProvider == "" {
    66  		c.HomeProvider = "/"
    67  	}
    68  
    69  	if len(c.Rules) == 0 {
    70  		c.Rules = map[string]rule{
    71  			"/": {
    72  				Address: sharedconf.GetGatewaySVC(""),
    73  			},
    74  			"00000000-0000-0000-0000-000000000000": {
    75  				Address: sharedconf.GetGatewaySVC(""),
    76  			},
    77  		}
    78  	}
    79  }
    80  
    81  func parseConfig(m map[string]interface{}) (*config, error) {
    82  	c := &config{}
    83  	if err := mapstructure.Decode(m, c); err != nil {
    84  		return nil, err
    85  	}
    86  	return c, nil
    87  }
    88  
    89  // New returns an implementation of the storage.Registry interface that
    90  // redirects requests to corresponding storage drivers.
    91  func New(m map[string]interface{}) (storage.Registry, error) {
    92  	c, err := parseConfig(m)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  	c.init()
    97  	return &reg{c: c}, nil
    98  }
    99  
   100  type reg struct {
   101  	c *config
   102  }
   103  
   104  func getProviderAddr(ctx context.Context, r rule) (string, string) {
   105  	addr := r.Address
   106  	if addr == "" {
   107  		if u, ok := ctxpkg.ContextGetUser(ctx); ok {
   108  			layout := templates.WithUser(u, r.Mapping)
   109  			for k, v := range r.Aliases {
   110  				if match, _ := regexp.MatchString("^"+k, layout); match {
   111  					return v.Address, v.ID
   112  				}
   113  			}
   114  		}
   115  	}
   116  	return addr, r.ProviderID
   117  }
   118  
   119  func (b *reg) GetProvider(ctx context.Context, space *provider.StorageSpace) (*registrypb.ProviderInfo, error) {
   120  	// Assume that HomeProvider is not a regexp
   121  	if space.SpaceType == "personal" {
   122  		if r, ok := b.c.Rules[b.c.HomeProvider]; ok {
   123  			if addr, id := getProviderAddr(ctx, r); addr != "" {
   124  				return &registrypb.ProviderInfo{
   125  					ProviderPath: b.c.HomeProvider,
   126  					ProviderId:   id,
   127  					Address:      addr,
   128  				}, nil
   129  			}
   130  		}
   131  		return nil, errors.New("static: home not found")
   132  	}
   133  	return nil, errors.New("static: only personal home is supported")
   134  	/*provider := []*registrypb.ProviderInfo{}
   135  	for k, v := range b.c.Rules {
   136  		if addr := getProviderAddr(ctx, v); addr != "" {
   137  			combs := generateRegexCombinations(k)
   138  			for _, c := range combs {
   139  				providers = append(providers, &registrypb.ProviderInfo{
   140  					ProviderPath: c,
   141  					Address:      addr,
   142  				})
   143  			}
   144  		}
   145  	}
   146  	return providers, nil
   147  	*/
   148  }
   149  
   150  func (b *reg) ListProviders(ctx context.Context, filters map[string]string) ([]*registrypb.ProviderInfo, error) {
   151  	// find longest match
   152  	var match *registrypb.ProviderInfo
   153  	var shardedMatches []*registrypb.ProviderInfo
   154  	// If the reference has a resource id set, use it to route
   155  	if filters["storage_id"] != "" {
   156  		for prefix, rule := range b.c.Rules {
   157  			addr, _ := getProviderAddr(ctx, rule)
   158  			r, err := regexp.Compile("^" + prefix + "$")
   159  			if err != nil {
   160  				continue
   161  			}
   162  			// TODO(labkode): fill path info based on provider id, if path and storage id points to same id, take that.
   163  			if m := r.FindString(filters["storage_id"]); m != "" {
   164  				return []*registrypb.ProviderInfo{{
   165  					ProviderId:   prefix,
   166  					Address:      addr,
   167  					ProviderPath: rule.ProviderPath,
   168  				}}, nil
   169  			}
   170  		}
   171  		// TODO if the storage id is not set but node id is set we could poll all storage providers to check if the node is known there
   172  		// for now, say the reference is invalid
   173  		if filters["opaque_id"] != "" {
   174  			return nil, errtypes.BadRequest(fmt.Sprintf("invalid filter %+v", filters))
   175  		}
   176  	}
   177  
   178  	// Try to find by path  as most storage operations will be done using the path.
   179  	// TODO this needs to be reevaluated once all clients query the storage registry for a list of storage providers
   180  	fn := path.Clean(filters["path"])
   181  	if fn != "" {
   182  		for prefix, rule := range b.c.Rules {
   183  			addr, id := getProviderAddr(ctx, rule)
   184  			r, err := regexp.Compile("^" + prefix)
   185  			if err != nil {
   186  				continue
   187  			}
   188  			if m := r.FindString(fn); m != "" {
   189  				if match != nil && len(match.ProviderPath) > len(m) {
   190  					// Do not overwrite existing longer match
   191  					continue
   192  				}
   193  				match = &registrypb.ProviderInfo{
   194  					ProviderId:   id,
   195  					ProviderPath: rule.ProviderPath,
   196  					Address:      addr,
   197  				}
   198  				if match.ProviderPath == "" {
   199  					match.ProviderPath = m
   200  				}
   201  			}
   202  			// Check if the current rule forms a part of a reference spread across storage providers.
   203  			if strings.HasPrefix(prefix, fn) {
   204  				combs := generateRegexCombinations(prefix)
   205  				for _, c := range combs {
   206  					shardedMatches = append(shardedMatches, &registrypb.ProviderInfo{
   207  						ProviderId:   id,
   208  						ProviderPath: c,
   209  						Address:      addr,
   210  					})
   211  				}
   212  			}
   213  		}
   214  	}
   215  
   216  	if match != nil && match.ProviderPath != "" {
   217  		return []*registrypb.ProviderInfo{match}, nil
   218  	} else if len(shardedMatches) > 0 {
   219  		// If we don't find a perfect match but at least one provider is encapsulated
   220  		// by the reference, return all such providers.
   221  		return shardedMatches, nil
   222  	}
   223  
   224  	return nil, errtypes.NotFound(fmt.Sprintf("storage provider not found for filters %+v", filters))
   225  
   226  }
   227  
   228  func generateRegexCombinations(rex string) []string {
   229  	m := bracketRegex.FindString(rex)
   230  	r := strings.Trim(strings.Trim(m, "["), "]")
   231  	if r == "" {
   232  		return []string{rex}
   233  	}
   234  	var combinations []string
   235  	for i := 0; i < len(r); i++ {
   236  		if i < len(r)-2 && r[i+1] == '-' {
   237  			for j := r[i]; j <= r[i+2]; j++ {
   238  				p := strings.Replace(rex, m, string(j), 1)
   239  				combinations = append(combinations, generateRegexCombinations(p)...)
   240  			}
   241  			i += 2
   242  		} else {
   243  			p := strings.Replace(rex, m, string(r[i]), 1)
   244  			combinations = append(combinations, generateRegexCombinations(p)...)
   245  		}
   246  	}
   247  	return combinations
   248  }