github.com/google/martian/v3@v3.3.3/proxyauth/proxyauth.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 proxyauth provides authentication support via the 16 // Proxy-Authorization header. 17 package proxyauth 18 19 import ( 20 "encoding/base64" 21 "net/http" 22 "strings" 23 24 "github.com/google/martian/v3" 25 "github.com/google/martian/v3/auth" 26 ) 27 28 var noop = martian.Noop("proxyauth.Modifier") 29 30 // Modifier is the proxy authentication modifier. 31 type Modifier struct { 32 reqmod martian.RequestModifier 33 resmod martian.ResponseModifier 34 } 35 36 // NewModifier returns a new proxy authentication modifier. 37 func NewModifier() *Modifier { 38 return &Modifier{ 39 reqmod: noop, 40 resmod: noop, 41 } 42 } 43 44 // SetRequestModifier sets the request modifier. 45 func (m *Modifier) SetRequestModifier(reqmod martian.RequestModifier) { 46 if reqmod == nil { 47 reqmod = noop 48 } 49 50 m.reqmod = reqmod 51 } 52 53 // SetResponseModifier sets the response modifier. 54 func (m *Modifier) SetResponseModifier(resmod martian.ResponseModifier) { 55 if resmod == nil { 56 resmod = noop 57 } 58 59 m.resmod = resmod 60 } 61 62 // ModifyRequest sets the auth ID in the context from the request iff it has 63 // not already been set and runs reqmod.ModifyRequest. If the underlying 64 // modifier has indicated via auth error that no valid auth credentials 65 // have been found we set ctx.SkipRoundTrip. 66 func (m *Modifier) ModifyRequest(req *http.Request) error { 67 ctx := martian.NewContext(req) 68 actx := auth.FromContext(ctx) 69 70 actx.SetID(id(req.Header)) 71 72 err := m.reqmod.ModifyRequest(req) 73 74 if actx.Error() != nil { 75 ctx.SkipRoundTrip() 76 } 77 78 return err 79 } 80 81 // ModifyResponse runs resmod.ModifyResponse and modifies the response to 82 // include the correct status code and headers if auth error is present. 83 // 84 // If an error is returned from resmod.ModifyResponse it is returned. 85 func (m *Modifier) ModifyResponse(res *http.Response) error { 86 ctx := martian.NewContext(res.Request) 87 actx := auth.FromContext(ctx) 88 89 err := m.resmod.ModifyResponse(res) 90 91 if actx.Error() != nil { 92 res.StatusCode = http.StatusProxyAuthRequired 93 res.Header.Set("Proxy-Authenticate", "Basic") 94 } 95 96 return err 97 } 98 99 // id returns an ID derived from the Proxy-Authorization header username and password. 100 func id(header http.Header) string { 101 id := strings.TrimPrefix(header.Get("Proxy-Authorization"), "Basic ") 102 103 data, err := base64.StdEncoding.DecodeString(id) 104 if err != nil { 105 return "" 106 } 107 108 return string(data) 109 }