soap

package module
v1.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 22, 2024 License: MPL-2.0 Imports: 22 Imported by: 0

README

gosoap

This library provides primitives for operating on a SOAP-based web service. The library supports encrypting the SOAP request using the WS-Security x.509 protocol, enabling SOAP calls against secured web services.

The following sub-features are currently enabled by default when WS-Security is enabled with the SignWith(...) method:

  • Include a wsse:SecurityTokenReference with the signature public key in form of a wsu:BinarySecurityToken
  • Automatically add a wsu:Timestamp with validity 10 seconds
  • Automatically generate a wsu:Id and use this #ID as URI to reference the respective signed element(s)
  • Sign Timestamp + Body elements of the SOAP message by default
  • Other elements can be signed (in theory) with the (currently unexported) addSignature() method
  • C14N canonicalization is ensured by marshaling the relevant to be signed sections with the github.com/m29h/xml package
  • SHA256 is used as default HMAC for signing purposes. More fine grained configuration on different signing profiles is planned for future release

Of course this library can also do basic SOAP (without WS-Security x.509)

A basic example usage would be as follows:

include (
    "context"
    "github.com/m29h/gosoap"
)

main() {
    certFile := "cert.pem"
    keyFile := "key.pem"

    wsseInfo, authErr := soap.NewWSSEAuthInfo(certFile, keyFile)
    if authErr != nil {
        fmt.Printf("Auth error: %s\n", authErr.Error())
        return
    }
    
    // setup Client and inject the HeaderBuilder for the wsse header which automatically
    // signs each message body and adds a signed Timestamp to limit message validity
    soapClient := soap.NewClient("https://soap.example.org/endpoint/service/", wsseInfo.Header())

    // Setup your request structure
    // ...
    //

    // Create the SOAP request
    // call.action is the SOAP action (i.e. method name)
    // call.requestData is the structure mapping to the SOAP request
    // call.responseData is an output structure mapping to the SOAP response

    // Make the request by invoking the SOAPdoer interface of the client
    // in a real-world example the SOAPdoer interface would get passed into a service type that would implement
    // the SOAP service methods

    err := soapClient.Do(context.Background(), call.action, call.requestData, call.responseData)

    if err != nil {
        fmt.Printf("Unable to validate: %s\n", err.Error())
        return
    } 
    
    // Now we can handle the response itself.
    // Do our custom processing
    // ...
    //
    
    fmt.Printf("Done!\n")
}

The code is very loosely based off the SOAP client https://github.com/textnow/gosoap.

Documentation

Overview

Package soap provides a modular SOAP client with the WS-Security x.509 protocol support including features such as wsu:Timestamp and wsu:BinarySecurityToken, enabling SOAP calls against secured web services

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidPEMFileSpecified is returned if the PEM file specified for WS signing is invalid
	ErrInvalidPEMFileSpecified = errors.New("invalid PEM key specified")
	// ErrEncryptedPEMFileSpecified is returnedd if the PEM file specified for WS signing is encrypted
	ErrEncryptedPEMFileSpecified = errors.New("encrypted PEM key specified")
	// ErrUnsupportedContentType is returned if we encounter a non-supported content type while querying
	ErrUnsupportedContentType = errors.New("unsupported content-type in response")
)
View Source
var (
	// ErrMultipartBodyEmpty is returned if a multi-part body that is empty is discovered
	ErrMultipartBodyEmpty = errors.New("multi-part body is empty")
	// ErrCannotSetBytesElement is an internal error that suggests our parse tree is malformed
	ErrCannotSetBytesElement = errors.New("cannot set the bytes element")
	// ErrMissingXOPPart is returned if the decoded body was missing the XOP header
	ErrMissingXOPPart = errors.New("did not find an xop part for this multipart message")
)
View Source
var (
	// ErrEnvelopeMisconfigured is returned if we attempt to deserialize a SOAP envelope without a type to deserialize the body or fault into.
	ErrEnvelopeMisconfigured = errors.New("envelope content or fault pointer empty")
)
View Source
var (
	// a fault body element was received
	ErrSoapFault = errors.New("soap fault")
)
View Source
var (
	// ErrUnableToSignEmptyEnvelope is returned if the envelope to be signed is empty. This is not valid.
	ErrUnableToSignEmptyEnvelope = errors.New("unable to sign, envelope is empty")
)

Functions

This section is empty.

Types

type Body

type Body struct {
	// XMLName is the serialized name of this object.
	XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
	// XMLNSWsu is the SOAP WS-Security utility namespace.
	WsuID string `xml:"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd Id,attr,omitempty"`

	// Fault is a SOAP fault we may detect in a response.
	Fault *Fault `xml:",omitempty"`
	// Body is a SOAP request or response body.
	Content []interface{} `xml:",omitempty"`
}

Body is a SOAP envelope body.

func (*Body) UnmarshalXML

func (b *Body) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error

UnmarshalXML is an overridden deserialization routine used to decode a SOAP envelope body. The elements are read from the decoder d, starting at the element start. The contents of the decode are stored in the invoking body b. Any errors encountered are returned.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is an opaque handle to a SOAP service.

func NewClient

func NewClient(url string, soapHeaders ...HeaderBuilder) *Client

NewClient creates a new Client that will access a SOAP service. Requests made using this client will all be wrapped in a SOAP envelope. See https://www.w3schools.com/xml/xml_soap.asp for more details. The default HTTP client used has no timeout nor circuit breaking. Override with SettHTTPClient. You have been warned.

func (*Client) Do

func (c *Client) Do(ctx context.Context, action string, request any, response any) error

Do invokes the SOAP request using its internal parameters. The request argument is serialized to XML, and if the call is successful the received XML is deserialized into the response argument. Any errors that are encountered are returned. If a SOAP fault is detected, then the 'details' property of the SOAP envelope will be appended into the faultDetailType argument.

func (*Client) SettHTTPClient

func (c *Client) SettHTTPClient(http *http.Client)

SettHTTPClient sets a custom http.Client instance to be used for all communications (e.g. for seting timeouts)

type DetailContainer

type DetailContainer struct {
	Detail interface{}
}

type Envelope

type Envelope struct {
	// XMLName is the serialized name of this object.
	XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`

	Header *Header
	Body   *Body
}

Envelope is a SOAP envelope.

func NewEnvelope

func NewEnvelope(content interface{}) *Envelope

NewEnvelope creates a new SOAP Envelope with the specified data as the content to serialize or deserialize. It defaults to a fault struct with no detail type. Content of the fault detail is wrapped into the error type. Headers are assumed to be omitted unless explicitly added via AddHeaders()

func (*Envelope) AddHeaders

func (e *Envelope) AddHeaders(elems ...any)

AddHeaders adds additional headers to be serialized to the resulting SOAP envelope.

type Fault

type Fault struct {
	// XMLName is the serialized name of this object.
	XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault"`

	Code   string `xml:"faultcode,omitempty"`
	String string `xml:"faultstring,omitempty"`
	Actor  string `xml:"faultactor,omitempty"`

	// DetailInternal is a handle to the internal fault detail type. Do not directly access;
	// this is made public only to allow for XML deserialization.
	// Use the Detail() method instead.
	DetailInternal *faultDetail `xml:"detail,omitempty"`
}

Fault is a SOAP fault code.

func NewFault

func NewFault() *Fault

NewFault returns a new XML fault struct

func (*Fault) Error

func (f *Fault) Error() string

Error satisfies the Error() interface allowing us to return a fault as an error.

func (*Fault) Unwrap

func (f *Fault) Unwrap() error

type FaultError

type FaultError interface {
	// ErrorString should return a short version of the detail as a string,
	// which will be used in place of <faultstring> for the error message.
	// Set "HasData()" to always return false if <faultstring> error
	// message is preferred.
	ErrorString() string
	// HasData indicates whether the composite fault contains any data.
	HasData() bool
}

type HTTPError

type HTTPError struct {
	//StatusCode is the status code returned in the HTTP response
	StatusCode int
	//ResponseBody contains the body returned in the HTTP response
	ResponseBody []byte
}

HTTPError is returned whenever the HTTP request to the server fails

func (*HTTPError) Error

func (e *HTTPError) Error() string
type Header struct {
	// XMLName is the serialized name of this object.
	XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Header"`
	// Headers is an array of envelope headers to send.
	Headers []interface{} `xml:",omitempty"`
}

Header is a SOAP envelope header.

type HeaderBuilder

type HeaderBuilder func(body any) (any, error)

HeaderBuilder is a function that takes a interface to the body and returns the finished header and an error

type MIMEMultipartAttachment

type MIMEMultipartAttachment struct {
	Name string
	Data []byte
}

type Request

type Request struct {
	// contains filtered or unexported fields
}

Request represents a single request to a SOAP service.

func NewRequest

func NewRequest(action string, url string, body interface{}, respType interface{}, faultType interface{}) *Request

NewRequest creates a SOAP request. This differs from a standard HTTP request in several ways. First, the SOAP library takes care of handling the envelope, so when the request is created the response and fault types are supplied so they can be properly parsed during envelope handling. Second, since we may perform WSSE signing on the request we do not supply a reader, instead the body is supplied here. If signing is desired, set the WSSE credentials on the request before passing it to the Client. NOTE: if custom SOAP headers are going to be supplied, they must be added before signing.

func (*Request) AddHeader

func (r *Request) AddHeader(header ...HeaderBuilder)

AddHeader adds the header argument to the list of elements set in the SOAP envelope Header element. This will be serialized to XML when the request is made to the service.

type SOAPBody

type SOAPBody struct {
	XMLName xml.Name `xml:"soap:Body"`

	Content interface{} `xml:",omitempty"`

	Fault *SOAPFault `xml:",omitempty"`
	// contains filtered or unexported fields
}

func (*SOAPBody) ErrorFromFault

func (b *SOAPBody) ErrorFromFault() error

type SOAPBodyResponse

type SOAPBodyResponse struct {
	XMLName xml.Name `xml:"Body"`

	Content interface{} `xml:",omitempty"`

	Fault *SOAPFault `xml:",omitempty"`
	// contains filtered or unexported fields
}

func (*SOAPBodyResponse) ErrorFromFault

func (b *SOAPBodyResponse) ErrorFromFault() error

func (*SOAPBodyResponse) UnmarshalXML

func (b *SOAPBodyResponse) UnmarshalXML(d *xml.Decoder, _ xml.StartElement) error

UnmarshalXML unmarshals SOAPBody xml

type SOAPDecoder

type SOAPDecoder interface {
	Decode(v interface{}) error
}

type SOAPEncoder

type SOAPEncoder interface {
	Encode(v interface{}) error
	Flush() error
}

type SOAPEnvelope

type SOAPEnvelope struct {
	XMLName xml.Name      `xml:"soap:Envelope"`
	XmlNS   string        `xml:"xmlns:soap,attr"`
	Headers []interface{} `xml:"soap:Header"`
	Body    SOAPBody
}

type SOAPEnvelopeResponse

type SOAPEnvelopeResponse struct {
	XMLName     xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
	Header      *SOAPHeaderResponse
	Body        SOAPBodyResponse
	Attachments []MIMEMultipartAttachment `xml:"attachments,omitempty"`
}

type SOAPFault

type SOAPFault struct {
	XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault"`

	Code   string     `xml:"faultcode,omitempty"`
	String string     `xml:"faultstring,omitempty"`
	Actor  string     `xml:"faultactor,omitempty"`
	Detail FaultError `xml:"detail,omitempty"`
}

func (*SOAPFault) Error

func (f *SOAPFault) Error() string

type SOAPHeaderResponse

type SOAPHeaderResponse struct {
	XMLName xml.Name `xml:"Header"`

	Headers []interface{}
}

type WSSEAuthInfo

type WSSEAuthInfo struct {
	// contains filtered or unexported fields
}

WSSEAuthInfo contains the information required to use WS-Security X.509 signing.

func NewWSSEAuthInfo

func NewWSSEAuthInfo(certPath string, keyPath string) (*WSSEAuthInfo, error)

NewWSSEAuthInfo retrieves the supplied certificate path and key path for signing SOAP requests. These requests will be secured using the WS-Security X.509 security standard.

func (*WSSEAuthInfo) Header

func (w *WSSEAuthInfo) Header() HeaderBuilder

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL