...

Source file src/github.com/go-kivik/kivik/v4/couchdb/chttp/proxyauth.go

Documentation: github.com/go-kivik/kivik/v4/couchdb/chttp

     1  // Licensed under the Apache License, Version 2.0 (the "License"); you may not
     2  // use this file except in compliance with the License. You may obtain a copy of
     3  // the License at
     4  //
     5  //  http://www.apache.org/licenses/LICENSE-2.0
     6  //
     7  // Unless required by applicable law or agreed to in writing, software
     8  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     9  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    10  // License for the specific language governing permissions and limitations under
    11  // the License.
    12  
    13  package chttp
    14  
    15  import (
    16  	"crypto/hmac"
    17  	"crypto/sha1"
    18  	"encoding/hex"
    19  	"fmt"
    20  	"net/http"
    21  	"strings"
    22  
    23  	"github.com/go-kivik/kivik/v4"
    24  )
    25  
    26  type proxyAuth struct {
    27  	Username string
    28  	Secret   string
    29  	Roles    []string
    30  	Headers  http.Header
    31  
    32  	transport http.RoundTripper
    33  	token     string
    34  }
    35  
    36  var (
    37  	_ authenticator = &proxyAuth{}
    38  	_ kivik.Option  = (*proxyAuth)(nil)
    39  )
    40  
    41  func (a *proxyAuth) Apply(target interface{}) {
    42  	if auth, ok := target.(*authenticator); ok {
    43  		// Clone this so that it's safe to re-use the same option to multiple
    44  		// client connections. TODO: This can no doubt be refactored.
    45  		*auth = &proxyAuth{
    46  			Username: a.Username,
    47  			Secret:   a.Secret,
    48  			Roles:    a.Roles,
    49  			Headers:  a.Headers,
    50  		}
    51  	}
    52  }
    53  
    54  func (a *proxyAuth) String() string {
    55  	return fmt.Sprintf("[ProxyAuth{username:%s,secret:%s}]", a.Username, strings.Repeat("*", len(a.Secret)))
    56  }
    57  
    58  func (a *proxyAuth) header(header string) string {
    59  	if h := a.Headers.Get(header); h != "" {
    60  		return http.CanonicalHeaderKey(h)
    61  	}
    62  	return header
    63  }
    64  
    65  func (a *proxyAuth) genToken() string {
    66  	if a.Secret == "" {
    67  		return ""
    68  	}
    69  	if a.token != "" {
    70  		return a.token
    71  	}
    72  	// Generate auth token
    73  	// https://docs.couchdb.org/en/stable/config/auth.html#couch_httpd_auth/x_auth_token
    74  	h := hmac.New(sha1.New, []byte(a.Secret))
    75  	_, _ = h.Write([]byte(a.Username))
    76  	a.token = hex.EncodeToString(h.Sum(nil))
    77  	return a.token
    78  }
    79  
    80  // RoundTrip implements the http.RoundTripper interface.
    81  func (a *proxyAuth) RoundTrip(req *http.Request) (*http.Response, error) {
    82  	if token := a.genToken(); token != "" {
    83  		req.Header.Set(a.header("X-Auth-CouchDB-Token"), token)
    84  	}
    85  
    86  	req.Header.Set(a.header("X-Auth-CouchDB-UserName"), a.Username)
    87  	req.Header.Set(a.header("X-Auth-CouchDB-Roles"), strings.Join(a.Roles, ","))
    88  
    89  	return a.transport.RoundTrip(req)
    90  }
    91  
    92  // Authenticate allows authentication via ProxyAuth.
    93  func (a *proxyAuth) Authenticate(c *Client) error {
    94  	a.transport = c.Transport
    95  	if a.transport == nil {
    96  		a.transport = http.DefaultTransport
    97  	}
    98  	c.Transport = a
    99  	return nil
   100  }
   101  

View as plain text