github.com/google/martian/v3@v3.3.3/martianurl/url_verifier.go (about)

     1  // Copyright 2015 Google Inc. All rights reserved.
     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  package martianurl
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  	"net/http"
    21  	"net/url"
    22  	"strings"
    23  
    24  	"github.com/google/martian/v3"
    25  	"github.com/google/martian/v3/parse"
    26  	"github.com/google/martian/v3/verify"
    27  )
    28  
    29  const (
    30  	errFormat     = "request(%s) url verify failure:\n%s"
    31  	errPartFormat = "\t%s: got %q, want %q"
    32  )
    33  
    34  func init() {
    35  	parse.Register("url.Verifier", verifierFromJSON)
    36  }
    37  
    38  // Verifier verifies the structure of URLs.
    39  type Verifier struct {
    40  	url *url.URL
    41  	err *martian.MultiError
    42  }
    43  
    44  type verifierJSON struct {
    45  	Scheme string               `json:"scheme"`
    46  	Host   string               `json:"host"`
    47  	Path   string               `json:"path"`
    48  	Query  string               `json:"query"`
    49  	Scope  []parse.ModifierType `json:"scope"`
    50  }
    51  
    52  // NewVerifier returns a new URL verifier.
    53  func NewVerifier(url *url.URL) verify.RequestVerifier {
    54  	return &Verifier{
    55  		url: url,
    56  		err: martian.NewMultiError(),
    57  	}
    58  }
    59  
    60  // ModifyRequest verifies that the request URL matches all parts of url. If the
    61  // value in url is non-empty it must be an exact match.
    62  func (v *Verifier) ModifyRequest(req *http.Request) error {
    63  	// skip requests to API
    64  	ctx := martian.NewContext(req)
    65  	if ctx.IsAPIRequest() {
    66  		return nil
    67  	}
    68  
    69  	var failures []string
    70  
    71  	u := req.URL
    72  
    73  	if v.url.Scheme != "" && v.url.Scheme != u.Scheme {
    74  		f := fmt.Sprintf(errPartFormat, "Scheme", u.Scheme, v.url.Scheme)
    75  		failures = append(failures, f)
    76  	}
    77  	if v.url.Host != "" && !MatchHost(u.Host, v.url.Host) {
    78  		f := fmt.Sprintf(errPartFormat, "Host", u.Host, v.url.Host)
    79  		failures = append(failures, f)
    80  	}
    81  	if v.url.Path != "" && v.url.Path != u.Path {
    82  		f := fmt.Sprintf(errPartFormat, "Path", u.Path, v.url.Path)
    83  		failures = append(failures, f)
    84  	}
    85  	if v.url.RawQuery != "" && v.url.RawQuery != u.RawQuery {
    86  		f := fmt.Sprintf(errPartFormat, "Query", u.RawQuery, v.url.RawQuery)
    87  		failures = append(failures, f)
    88  	}
    89  	if v.url.Fragment != "" && v.url.Fragment != u.Fragment {
    90  		f := fmt.Sprintf(errPartFormat, "Fragment", u.Fragment, v.url.Fragment)
    91  		failures = append(failures, f)
    92  	}
    93  
    94  	if len(failures) > 0 {
    95  		err := fmt.Errorf(errFormat, u, strings.Join(failures, "\n"))
    96  		v.err.Add(err)
    97  	}
    98  
    99  	return nil
   100  }
   101  
   102  // VerifyRequests returns an error if verification for any request failed.
   103  // If an error is returned it will be of type *martian.MultiError.
   104  func (v *Verifier) VerifyRequests() error {
   105  	if v.err.Empty() {
   106  		return nil
   107  	}
   108  
   109  	return v.err
   110  }
   111  
   112  // ResetRequestVerifications clears all failed request verifications.
   113  func (v *Verifier) ResetRequestVerifications() {
   114  	v.err = martian.NewMultiError()
   115  }
   116  
   117  // verifierFromJSON builds a martianurl.Verifier from JSON.
   118  //
   119  // Example modifier JSON:
   120  // {
   121  //   "martianurl.Verifier": {
   122  //     "scope": ["request"],
   123  //     "scheme": "https",
   124  //     "host": "www.google.com",
   125  //     "path": "/proxy",
   126  //     "query": "testing=true"
   127  //   }
   128  // }
   129  func verifierFromJSON(b []byte) (*parse.Result, error) {
   130  	msg := &verifierJSON{}
   131  	if err := json.Unmarshal(b, msg); err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	v := NewVerifier(&url.URL{
   136  		Scheme:   msg.Scheme,
   137  		Host:     msg.Host,
   138  		Path:     msg.Path,
   139  		RawQuery: msg.Query,
   140  	})
   141  
   142  	return parse.NewResult(v, msg.Scope)
   143  }