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  }