github.com/dbernstein1/tyk@v2.9.0-beta9-dl-apic+incompatible/apidef/importer/wsdl.go (about) 1 package importer 2 3 import ( 4 "encoding/xml" 5 "errors" 6 "io" 7 "net/http" 8 "strings" 9 10 "github.com/TykTechnologies/tyk/apidef" 11 uuid "github.com/satori/go.uuid" 12 ) 13 14 const WSDLSource APIImporterSource = "wsdl" 15 16 var portName = map[string]string{} 17 var bindingList = map[string]*WSDLBinding{} 18 19 func (*WSDLDef) SetServicePortMapping(input map[string]string) { 20 for k, v := range input { 21 portName[k] = v 22 } 23 } 24 25 const ( 26 NS_WSDL20 = "http://www.w3.org/ns/wsdl" 27 NS_WSDL = "http://schemas.xmlsoap.org/wsdl/" 28 NS_SOAP = "http://schemas.xmlsoap.org/wsdl/soap/" 29 NS_SOAP12 = "http://schemas.xmlsoap.org/wsdl/soap12/" 30 NS_HTTP = "http://schemas.xmlsoap.org/wsdl/http/" 31 ) 32 33 const ( 34 PROT_HTTP = "http" 35 PROT_SOAP = "soap" 36 PROT_SOAP_12 = "soap12" 37 ) 38 39 type WSDLDef struct { 40 Definition WSDL `xml:"http://schemas.xmlsoap.org/wsdl/ definitions"` 41 } 42 43 type WSDL struct { 44 Services []*WSDLService `xml:"http://schemas.xmlsoap.org/wsdl/ service"` 45 Bindings []*WSDLBinding `xml:"http://schemas.xmlsoap.org/wsdl/ binding"` 46 } 47 48 type WSDLService struct { 49 Name string `xml:"name,attr"` 50 Ports []*WSDLPort `xml:"http://schemas.xmlsoap.org/wsdl/ port"` 51 } 52 53 type WSDLPort struct { 54 Name string `xml:"name,attr"` 55 Binding string `xml:"binding,attr"` 56 Address WSDLAddress `xml:"address"` 57 } 58 59 type WSDLAddress struct { 60 Location string `xml:"location,attr"` 61 } 62 63 type WSDLBinding struct { 64 Name string `xml:"name,attr"` 65 Operations []*WSDLOperation `xml:"http://schemas.xmlsoap.org/wsdl/ operation"` 66 Protocol string 67 Method string 68 isSupportedProtocol bool 69 } 70 71 type WSDLOperation struct { 72 Name string `xml:"name,attr"` 73 Endpoint string 74 IsUrlReplacement bool 75 } 76 77 func (def *WSDLDef) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 78 if start.Name.Space == NS_WSDL20 { 79 return errors.New("WSDL 2.0 is not supported") 80 } else if start.Name.Space == NS_WSDL && start.Name.Local == "definitions" { 81 return d.DecodeElement(&def.Definition, &start) 82 } else { 83 return errors.New("Invalid WSDL file. WSDL definition must start contain <definitions> element") 84 } 85 } 86 87 func (b *WSDLBinding) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 88 //Get value of name attribute 89 for _, attr := range start.Attr { 90 if attr.Name.Local == "name" { 91 b.Name = attr.Value 92 break 93 } 94 } 95 96 if b.Name == "" { 97 return errors.New("Binding name is empty. Malformed wsdl") 98 } 99 100 //Fetch protocol specific data 101 //If soap/soap12 is used, set Method to POST 102 //If http is used, get value of verb attribute 103 //If any other protocol is used, then skip 104 for { 105 tok, err := d.Token() 106 if err != nil { 107 log.Error("Error will parsing WSDL file: ", err) 108 return err 109 } 110 111 switch t := tok.(type) { 112 case xml.StartElement: 113 { 114 switch t.Name.Local { 115 case "binding": 116 { 117 switch t.Name.Space { 118 case NS_SOAP, NS_SOAP12: 119 { 120 b.isSupportedProtocol = true 121 if t.Name.Space == NS_SOAP { 122 b.Protocol = PROT_SOAP 123 } else { 124 b.Protocol = PROT_SOAP_12 125 } 126 127 //Get transport protocol 128 //TODO if transport protocol is different from http 129 var transport string 130 for _, attr := range t.Attr { 131 if attr.Name.Local == "transport" { 132 transport = attr.Value 133 break 134 } 135 } 136 parts := strings.Split(transport, "/") 137 if parts[len(parts)-1] == "http" { 138 b.Method = http.MethodPost 139 } else { 140 b.isSupportedProtocol = false 141 } 142 143 } 144 case NS_HTTP: 145 { 146 b.isSupportedProtocol = true 147 b.Protocol = PROT_HTTP 148 for _, attr := range t.Attr { 149 if attr.Name.Local == "verb" { 150 b.Method = attr.Value 151 break 152 } 153 } 154 155 } 156 default: 157 { 158 log.Debugf("Unsupported binding protocol is used %s:%s", t.Name.Space, t.Name.Local) 159 b.isSupportedProtocol = false 160 return nil 161 } 162 } 163 } 164 case "operation": 165 { 166 if t.Name.Space == NS_WSDL && b.isSupportedProtocol { 167 op := new(WSDLOperation) 168 if err := d.DecodeElement(op, &t); err != nil { 169 return err 170 } 171 b.Operations = append(b.Operations, op) 172 } 173 } 174 default: 175 { 176 if err := d.Skip(); err != nil { 177 return err 178 } 179 } 180 } 181 } 182 case xml.EndElement: 183 { 184 if t.Name.Space == NS_WSDL && t.Name.Local == "binding" { 185 bindingList[b.Name] = b 186 return nil 187 } 188 } 189 } 190 } 191 } 192 193 func (op *WSDLOperation) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 194 for _, attr := range start.Attr { 195 if attr.Name.Local == "name" { 196 op.Name = attr.Value 197 break 198 } 199 } 200 201 if op.Name == "" { 202 return errors.New("Operation name is empty. Malformed wsdl") 203 } 204 205 var protocol string 206 207 for { 208 tok, err := d.Token() 209 if err != nil { 210 return err 211 } 212 213 switch t := tok.(type) { 214 case xml.StartElement: 215 { 216 if t.Name.Local == "operation" { 217 switch t.Name.Space { 218 case NS_SOAP, NS_SOAP12: 219 { 220 protocol = PROT_SOAP 221 break 222 } 223 case NS_HTTP: 224 { 225 protocol = PROT_HTTP 226 for _, attr := range t.Attr { 227 if attr.Name.Local == "location" { 228 op.Endpoint = attr.Value 229 break 230 } 231 } 232 break 233 } 234 default: 235 { 236 if err := d.Skip(); err != nil { 237 return err 238 } 239 } 240 241 } 242 } 243 244 if protocol == PROT_HTTP { 245 if t.Name.Local == "urlReplacement" { 246 op.IsUrlReplacement = true 247 endpoint := op.Endpoint 248 tmp := strings.Replace(endpoint, "(", "{", -1) 249 new_endpoint := strings.Replace(tmp, ")", "}", -1) 250 251 op.Endpoint = new_endpoint 252 253 } 254 } else { 255 if err := d.Skip(); err != nil { 256 return err 257 } 258 } 259 } 260 case xml.EndElement: 261 { 262 if t.Name.Space == NS_WSDL && t.Name.Local == "operation" { 263 return nil 264 } 265 } 266 } 267 268 } 269 } 270 271 func (s *WSDLDef) LoadFrom(r io.Reader) error { 272 return xml.NewDecoder(r).Decode(&s) 273 } 274 275 func (def *WSDLDef) ToAPIDefinition(orgId, upstreamURL string, as_mock bool) (*apidef.APIDefinition, error) { 276 ad := apidef.APIDefinition{ 277 Name: def.Definition.Services[0].Name, 278 Active: true, 279 UseKeylessAccess: true, 280 OrgID: orgId, 281 APIID: uuid.NewV4().String(), 282 } 283 284 ad.VersionDefinition.Key = "version" 285 ad.VersionDefinition.Location = "header" 286 ad.VersionData.Versions = make(map[string]apidef.VersionInfo) 287 ad.Proxy.ListenPath = "/" + def.Definition.Services[0].Name + "/" 288 ad.Proxy.StripListenPath = true 289 ad.Proxy.TargetURL = upstreamURL 290 291 if as_mock { 292 log.Warning("Mocks not supported for WSDL definitions, ignoring option") 293 } 294 295 versionData, err := def.ConvertIntoApiVersion(false) 296 if err != nil { 297 return nil, err 298 } 299 300 def.InsertIntoAPIDefinitionAsVersion(versionData, &ad, "1.0.0") 301 ad.VersionData.DefaultVersion = "1.0.0" 302 return &ad, nil 303 } 304 305 func trimNamespace(s string) string { 306 parts := strings.SplitN(s, ":", 2) 307 if len(parts) == 1 { 308 return parts[0] 309 } else { 310 return parts[1] 311 } 312 } 313 314 func (def *WSDLDef) ConvertIntoApiVersion(bool) (apidef.VersionInfo, error) { 315 versionInfo := apidef.VersionInfo{} 316 versionInfo.UseExtendedPaths = true 317 versionInfo.Name = "1.0.0" 318 versionInfo.ExtendedPaths.TrackEndpoints = make([]apidef.TrackEndpointMeta, 0) 319 versionInfo.ExtendedPaths.URLRewrite = make([]apidef.URLRewriteMeta, 0) 320 versionInfo.ExtendedPaths.Internal = make([]apidef.InternalMeta, 0) 321 322 var foundPort bool 323 var serviceCount int 324 325 for _, service := range def.Definition.Services { 326 foundPort = false 327 if service.Name == "" { 328 continue 329 } 330 for _, port := range service.Ports { 331 portName := portName[service.Name] 332 if portName == "" { 333 portName = service.Ports[0].Name 334 } 335 if port.Name == portName { 336 foundPort = true 337 338 bindingName := trimNamespace(port.Binding) 339 340 binding := bindingList[bindingName] 341 if binding == nil { 342 log.Errorf("Binding for port %s of service %s not found. Termination processing of the service", port.Name, service.Name) 343 344 foundPort = false 345 break 346 } 347 348 if !binding.isSupportedProtocol { 349 log.Errorf("Unsupported transport protocol. Skipping process of the service %s", service.Name) 350 foundPort = false 351 break 352 } 353 354 if len(binding.Operations) == 0 { 355 log.Errorf("No operation found for binding %s of service %s\n", binding.Name, service.Name) 356 break 357 } 358 359 serviceCount++ 360 method := binding.Method 361 362 //Create endpoints for each operation 363 for _, op := range binding.Operations { 364 operationTrackEndpoint := apidef.TrackEndpointMeta{} 365 operationUrlRewrite := apidef.URLRewriteMeta{} 366 path := "" 367 368 if binding.Protocol == PROT_HTTP { 369 if op.Endpoint[0] == '/' { 370 path = service.Name + op.Endpoint 371 } else { 372 path = service.Name + "/" + op.Endpoint 373 } 374 } else { 375 path = service.Name + "/" + op.Name 376 } 377 378 //Add each operation in trackendpoint 379 operationTrackEndpoint.Path = path 380 operationTrackEndpoint.Method = method 381 382 versionInfo.ExtendedPaths.TrackEndpoints = append(versionInfo.ExtendedPaths.TrackEndpoints, operationTrackEndpoint) 383 384 //Rewrite operation to service endpoint 385 operationUrlRewrite.Method = method 386 operationUrlRewrite.Path = path 387 388 if binding.Protocol == PROT_HTTP { 389 if op.IsUrlReplacement == true { 390 pattern := ReplaceWildCards(op.Endpoint) 391 operationUrlRewrite.MatchPattern = "(" + pattern + ")" 392 } else { 393 operationUrlRewrite.MatchPattern = "(" + op.Endpoint + ".*)" 394 } 395 operationUrlRewrite.RewriteTo = port.Address.Location + "$1" 396 } else { 397 operationUrlRewrite.MatchPattern = path 398 operationUrlRewrite.RewriteTo = port.Address.Location 399 } 400 401 versionInfo.ExtendedPaths.URLRewrite = append(versionInfo.ExtendedPaths.URLRewrite, operationUrlRewrite) 402 } 403 404 break 405 } 406 } 407 408 if foundPort == false { 409 log.Errorf("Port for service %s not found. Skiping processing of the service", service.Name) 410 } 411 } 412 413 if serviceCount == 0 { 414 return versionInfo, errors.New("Error processing wsdl file") 415 } 416 417 return versionInfo, nil 418 } 419 420 func (def *WSDLDef) InsertIntoAPIDefinitionAsVersion(version apidef.VersionInfo, apidef *apidef.APIDefinition, versionName string) error { 421 apidef.VersionData.NotVersioned = false 422 apidef.VersionData.Versions[versionName] = version 423 return nil 424 } 425 426 func ReplaceWildCards(endpoint string) string { 427 var result []rune 428 var inside bool 429 430 for _, s := range endpoint { 431 if s == '{' { 432 inside = true 433 continue 434 } else if s == '}' { 435 inside = false 436 result = append(result, '.', '*') 437 continue 438 } 439 440 if inside == false { 441 result = append(result, s) 442 } 443 } 444 return string(result) 445 }