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 ®{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 ®istrypb.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, ®istrypb.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 = ®istrypb.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, ®istrypb.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 }