mirror of
				https://github.com/vbatts/srvdav.git
				synced 2025-10-27 02:46:42 +00:00 
			
		
		
		
	vendor: dep init
Signed-off-by: Vincent Batts <vbatts@hashbangbash.com>
This commit is contained in:
		
							parent
							
								
									83a1e114b0
								
							
						
					
					
						commit
						6feae25207
					
				
					 945 changed files with 238318 additions and 0 deletions
				
			
		
							
								
								
									
										5
									
								
								vendor/github.com/abbot/go-http-auth/.gitignore
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/abbot/go-http-auth/.gitignore
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| *~ | ||||
| *.a | ||||
| *.6 | ||||
| *.out | ||||
| _testmain.go | ||||
							
								
								
									
										178
									
								
								vendor/github.com/abbot/go-http-auth/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								vendor/github.com/abbot/go-http-auth/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,178 @@ | |||
| 
 | ||||
|                                  Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|                         http://www.apache.org/licenses/ | ||||
| 
 | ||||
|    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||
| 
 | ||||
|    1. Definitions. | ||||
| 
 | ||||
|       "License" shall mean the terms and conditions for use, reproduction, | ||||
|       and distribution as defined by Sections 1 through 9 of this document. | ||||
| 
 | ||||
|       "Licensor" shall mean the copyright owner or entity authorized by | ||||
|       the copyright owner that is granting the License. | ||||
| 
 | ||||
|       "Legal Entity" shall mean the union of the acting entity and all | ||||
|       other entities that control, are controlled by, or are under common | ||||
|       control with that entity. For the purposes of this definition, | ||||
|       "control" means (i) the power, direct or indirect, to cause the | ||||
|       direction or management of such entity, whether by contract or | ||||
|       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||
|       outstanding shares, or (iii) beneficial ownership of such entity. | ||||
| 
 | ||||
|       "You" (or "Your") shall mean an individual or Legal Entity | ||||
|       exercising permissions granted by this License. | ||||
| 
 | ||||
|       "Source" form shall mean the preferred form for making modifications, | ||||
|       including but not limited to software source code, documentation | ||||
|       source, and configuration files. | ||||
| 
 | ||||
|       "Object" form shall mean any form resulting from mechanical | ||||
|       transformation or translation of a Source form, including but | ||||
|       not limited to compiled object code, generated documentation, | ||||
|       and conversions to other media types. | ||||
| 
 | ||||
|       "Work" shall mean the work of authorship, whether in Source or | ||||
|       Object form, made available under the License, as indicated by a | ||||
|       copyright notice that is included in or attached to the work | ||||
|       (an example is provided in the Appendix below). | ||||
| 
 | ||||
|       "Derivative Works" shall mean any work, whether in Source or Object | ||||
|       form, that is based on (or derived from) the Work and for which the | ||||
|       editorial revisions, annotations, elaborations, or other modifications | ||||
|       represent, as a whole, an original work of authorship. For the purposes | ||||
|       of this License, Derivative Works shall not include works that remain | ||||
|       separable from, or merely link (or bind by name) to the interfaces of, | ||||
|       the Work and Derivative Works thereof. | ||||
| 
 | ||||
|       "Contribution" shall mean any work of authorship, including | ||||
|       the original version of the Work and any modifications or additions | ||||
|       to that Work or Derivative Works thereof, that is intentionally | ||||
|       submitted to Licensor for inclusion in the Work by the copyright owner | ||||
|       or by an individual or Legal Entity authorized to submit on behalf of | ||||
|       the copyright owner. For the purposes of this definition, "submitted" | ||||
|       means any form of electronic, verbal, or written communication sent | ||||
|       to the Licensor or its representatives, including but not limited to | ||||
|       communication on electronic mailing lists, source code control systems, | ||||
|       and issue tracking systems that are managed by, or on behalf of, the | ||||
|       Licensor for the purpose of discussing and improving the Work, but | ||||
|       excluding communication that is conspicuously marked or otherwise | ||||
|       designated in writing by the copyright owner as "Not a Contribution." | ||||
| 
 | ||||
|       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||
|       on behalf of whom a Contribution has been received by Licensor and | ||||
|       subsequently incorporated within the Work. | ||||
| 
 | ||||
|    2. Grant of Copyright License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       copyright license to reproduce, prepare Derivative Works of, | ||||
|       publicly display, publicly perform, sublicense, and distribute the | ||||
|       Work and such Derivative Works in Source or Object form. | ||||
| 
 | ||||
|    3. Grant of Patent License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       (except as stated in this section) patent license to make, have made, | ||||
|       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||
|       where such license applies only to those patent claims licensable | ||||
|       by such Contributor that are necessarily infringed by their | ||||
|       Contribution(s) alone or by combination of their Contribution(s) | ||||
|       with the Work to which such Contribution(s) was submitted. If You | ||||
|       institute patent litigation against any entity (including a | ||||
|       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||
|       or a Contribution incorporated within the Work constitutes direct | ||||
|       or contributory patent infringement, then any patent licenses | ||||
|       granted to You under this License for that Work shall terminate | ||||
|       as of the date such litigation is filed. | ||||
| 
 | ||||
|    4. Redistribution. You may reproduce and distribute copies of the | ||||
|       Work or Derivative Works thereof in any medium, with or without | ||||
|       modifications, and in Source or Object form, provided that You | ||||
|       meet the following conditions: | ||||
| 
 | ||||
|       (a) You must give any other recipients of the Work or | ||||
|           Derivative Works a copy of this License; and | ||||
| 
 | ||||
|       (b) You must cause any modified files to carry prominent notices | ||||
|           stating that You changed the files; and | ||||
| 
 | ||||
|       (c) You must retain, in the Source form of any Derivative Works | ||||
|           that You distribute, all copyright, patent, trademark, and | ||||
|           attribution notices from the Source form of the Work, | ||||
|           excluding those notices that do not pertain to any part of | ||||
|           the Derivative Works; and | ||||
| 
 | ||||
|       (d) If the Work includes a "NOTICE" text file as part of its | ||||
|           distribution, then any Derivative Works that You distribute must | ||||
|           include a readable copy of the attribution notices contained | ||||
|           within such NOTICE file, excluding those notices that do not | ||||
|           pertain to any part of the Derivative Works, in at least one | ||||
|           of the following places: within a NOTICE text file distributed | ||||
|           as part of the Derivative Works; within the Source form or | ||||
|           documentation, if provided along with the Derivative Works; or, | ||||
|           within a display generated by the Derivative Works, if and | ||||
|           wherever such third-party notices normally appear. The contents | ||||
|           of the NOTICE file are for informational purposes only and | ||||
|           do not modify the License. You may add Your own attribution | ||||
|           notices within Derivative Works that You distribute, alongside | ||||
|           or as an addendum to the NOTICE text from the Work, provided | ||||
|           that such additional attribution notices cannot be construed | ||||
|           as modifying the License. | ||||
| 
 | ||||
|       You may add Your own copyright statement to Your modifications and | ||||
|       may provide additional or different license terms and conditions | ||||
|       for use, reproduction, or distribution of Your modifications, or | ||||
|       for any such Derivative Works as a whole, provided Your use, | ||||
|       reproduction, and distribution of the Work otherwise complies with | ||||
|       the conditions stated in this License. | ||||
| 
 | ||||
|    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||
|       any Contribution intentionally submitted for inclusion in the Work | ||||
|       by You to the Licensor shall be under the terms and conditions of | ||||
|       this License, without any additional terms or conditions. | ||||
|       Notwithstanding the above, nothing herein shall supersede or modify | ||||
|       the terms of any separate license agreement you may have executed | ||||
|       with Licensor regarding such Contributions. | ||||
| 
 | ||||
|    6. Trademarks. This License does not grant permission to use the trade | ||||
|       names, trademarks, service marks, or product names of the Licensor, | ||||
|       except as required for reasonable and customary use in describing the | ||||
|       origin of the Work and reproducing the content of the NOTICE file. | ||||
| 
 | ||||
|    7. Disclaimer of Warranty. Unless required by applicable law or | ||||
|       agreed to in writing, Licensor provides the Work (and each | ||||
|       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||
|       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||
|       implied, including, without limitation, any warranties or conditions | ||||
|       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||
|       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||
|       appropriateness of using or redistributing the Work and assume any | ||||
|       risks associated with Your exercise of permissions under this License. | ||||
| 
 | ||||
|    8. Limitation of Liability. In no event and under no legal theory, | ||||
|       whether in tort (including negligence), contract, or otherwise, | ||||
|       unless required by applicable law (such as deliberate and grossly | ||||
|       negligent acts) or agreed to in writing, shall any Contributor be | ||||
|       liable to You for damages, including any direct, indirect, special, | ||||
|       incidental, or consequential damages of any character arising as a | ||||
|       result of this License or out of the use or inability to use the | ||||
|       Work (including but not limited to damages for loss of goodwill, | ||||
|       work stoppage, computer failure or malfunction, or any and all | ||||
|       other commercial damages or losses), even if such Contributor | ||||
|       has been advised of the possibility of such damages. | ||||
| 
 | ||||
|    9. Accepting Warranty or Additional Liability. While redistributing | ||||
|       the Work or Derivative Works thereof, You may choose to offer, | ||||
|       and charge a fee for, acceptance of support, warranty, indemnity, | ||||
|       or other liability obligations and/or rights consistent with this | ||||
|       License. However, in accepting such obligations, You may act only | ||||
|       on Your own behalf and on Your sole responsibility, not on behalf | ||||
|       of any other Contributor, and only if You agree to indemnify, | ||||
|       defend, and hold each Contributor harmless for any liability | ||||
|       incurred by, or claims asserted against, such Contributor by reason | ||||
|       of your accepting any such warranty or additional liability. | ||||
| 
 | ||||
|    END OF TERMS AND CONDITIONS | ||||
| 
 | ||||
							
								
								
									
										12
									
								
								vendor/github.com/abbot/go-http-auth/Makefile
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/abbot/go-http-auth/Makefile
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| include $(GOROOT)/src/Make.inc | ||||
| 
 | ||||
| TARG=auth_digest | ||||
| GOFILES=\
 | ||||
| 	auth.go\
 | ||||
| 	digest.go\
 | ||||
| 	basic.go\
 | ||||
| 	misc.go\
 | ||||
| 	md5crypt.go\
 | ||||
| 	users.go\
 | ||||
| 
 | ||||
| include $(GOROOT)/src/Make.pkg | ||||
							
								
								
									
										71
									
								
								vendor/github.com/abbot/go-http-auth/README.md
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								vendor/github.com/abbot/go-http-auth/README.md
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,71 @@ | |||
| HTTP Authentication implementation in Go | ||||
| ======================================== | ||||
| 
 | ||||
| This is an implementation of HTTP Basic and HTTP Digest authentication | ||||
| in Go language. It is designed as a simple wrapper for | ||||
| http.RequestHandler functions. | ||||
| 
 | ||||
| Features | ||||
| -------- | ||||
|   | ||||
|  * Supports HTTP Basic and HTTP Digest authentication. | ||||
|  * Supports htpasswd and htdigest formatted files. | ||||
|  * Automatic reloading of password files. | ||||
|  * Pluggable interface for user/password storage. | ||||
|  * Supports MD5, SHA1 and BCrypt for Basic authentication password storage. | ||||
|  * Configurable Digest nonce cache size with expiration. | ||||
|  * Wrapper for legacy http handlers (http.HandlerFunc interface) | ||||
|   | ||||
| Example usage | ||||
| ------------- | ||||
| 
 | ||||
| This is a complete working example for Basic auth: | ||||
| 
 | ||||
|     package main | ||||
| 
 | ||||
|     import ( | ||||
|             "fmt" | ||||
|             "net/http" | ||||
| 
 | ||||
|             auth "github.com/abbot/go-http-auth" | ||||
|     ) | ||||
| 
 | ||||
|     func Secret(user, realm string) string { | ||||
|             if user == "john" { | ||||
|                     // password is "hello" | ||||
|                     return "$1$dlPL2MqE$oQmn16q49SqdmhenQuNgs1" | ||||
|             } | ||||
|             return "" | ||||
|     } | ||||
| 
 | ||||
|     func handle(w http.ResponseWriter, r *auth.AuthenticatedRequest) { | ||||
|             fmt.Fprintf(w, "<html><body><h1>Hello, %s!</h1></body></html>", r.Username) | ||||
|     } | ||||
| 
 | ||||
|     func main() { | ||||
|             authenticator := auth.NewBasicAuthenticator("example.com", Secret) | ||||
|             http.HandleFunc("/", authenticator.Wrap(handle)) | ||||
|             http.ListenAndServe(":8080", nil) | ||||
|     } | ||||
| 
 | ||||
| See more examples in the "examples" directory. | ||||
| 
 | ||||
| Legal | ||||
| ----- | ||||
| 
 | ||||
| This module is developed under Apache 2.0 license, and can be used for | ||||
| open and proprietary projects. | ||||
| 
 | ||||
| Copyright 2012-2013 Lev Shamardin | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); you | ||||
| may not use this file or any other part of this project except in | ||||
| compliance with the License. You may obtain a copy of the License at | ||||
| 
 | ||||
| http://www.apache.org/licenses/LICENSE-2.0 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||
| implied. See the License for the specific language governing | ||||
| permissions and limitations under the License. | ||||
							
								
								
									
										109
									
								
								vendor/github.com/abbot/go-http-auth/auth.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								vendor/github.com/abbot/go-http-auth/auth.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,109 @@ | |||
| // Package auth is an implementation of HTTP Basic and HTTP Digest authentication. | ||||
| package auth | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 
 | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
| 
 | ||||
| /* | ||||
|  Request handlers must take AuthenticatedRequest instead of http.Request | ||||
| */ | ||||
| type AuthenticatedRequest struct { | ||||
| 	http.Request | ||||
| 	/* | ||||
| 	 Authenticated user name. Current API implies that Username is | ||||
| 	 never empty, which means that authentication is always done | ||||
| 	 before calling the request handler. | ||||
| 	*/ | ||||
| 	Username string | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  AuthenticatedHandlerFunc is like http.HandlerFunc, but takes | ||||
|  AuthenticatedRequest instead of http.Request | ||||
| */ | ||||
| type AuthenticatedHandlerFunc func(http.ResponseWriter, *AuthenticatedRequest) | ||||
| 
 | ||||
| /* | ||||
|  Authenticator wraps an AuthenticatedHandlerFunc with | ||||
|  authentication-checking code. | ||||
| 
 | ||||
|  Typical Authenticator usage is something like: | ||||
| 
 | ||||
|    authenticator := SomeAuthenticator(...) | ||||
|    http.HandleFunc("/", authenticator(my_handler)) | ||||
| 
 | ||||
|  Authenticator wrapper checks the user authentication and calls the | ||||
|  wrapped function only after authentication has succeeded. Otherwise, | ||||
|  it returns a handler which initiates the authentication procedure. | ||||
| */ | ||||
| type Authenticator func(AuthenticatedHandlerFunc) http.HandlerFunc | ||||
| 
 | ||||
| // Info contains authentication information for the request. | ||||
| type Info struct { | ||||
| 	// Authenticated is set to true when request was authenticated | ||||
| 	// successfully, i.e. username and password passed in request did | ||||
| 	// pass the check. | ||||
| 	Authenticated bool | ||||
| 
 | ||||
| 	// Username contains a user name passed in the request when | ||||
| 	// Authenticated is true. It's value is undefined if Authenticated | ||||
| 	// is false. | ||||
| 	Username string | ||||
| 
 | ||||
| 	// ResponseHeaders contains extra headers that must be set by server | ||||
| 	// when sending back HTTP response. | ||||
| 	ResponseHeaders http.Header | ||||
| } | ||||
| 
 | ||||
| // UpdateHeaders updates headers with this Info's ResponseHeaders. It is | ||||
| // safe to call this function on nil Info. | ||||
| func (i *Info) UpdateHeaders(headers http.Header) { | ||||
| 	if i == nil { | ||||
| 		return | ||||
| 	} | ||||
| 	for k, values := range i.ResponseHeaders { | ||||
| 		for _, v := range values { | ||||
| 			headers.Add(k, v) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| type key int // used for context keys | ||||
| 
 | ||||
| var infoKey key = 0 | ||||
| 
 | ||||
| type AuthenticatorInterface interface { | ||||
| 	// NewContext returns a new context carrying authentication | ||||
| 	// information extracted from the request. | ||||
| 	NewContext(ctx context.Context, r *http.Request) context.Context | ||||
| 
 | ||||
| 	// Wrap returns an http.HandlerFunc which wraps | ||||
| 	// AuthenticatedHandlerFunc with this authenticator's | ||||
| 	// authentication checks. | ||||
| 	Wrap(AuthenticatedHandlerFunc) http.HandlerFunc | ||||
| } | ||||
| 
 | ||||
| // FromContext returns authentication information from the context or | ||||
| // nil if no such information present. | ||||
| func FromContext(ctx context.Context) *Info { | ||||
| 	info, ok := ctx.Value(infoKey).(*Info) | ||||
| 	if !ok { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return info | ||||
| } | ||||
| 
 | ||||
| // AuthUsernameHeader is the header set by JustCheck functions. It | ||||
| // contains an authenticated username (if authentication was | ||||
| // successful). | ||||
| const AuthUsernameHeader = "X-Authenticated-Username" | ||||
| 
 | ||||
| func JustCheck(auth AuthenticatorInterface, wrapped http.HandlerFunc) http.HandlerFunc { | ||||
| 	return auth.Wrap(func(w http.ResponseWriter, ar *AuthenticatedRequest) { | ||||
| 		ar.Header.Set(AuthUsernameHeader, ar.Username) | ||||
| 		wrapped(w, &ar.Request) | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										163
									
								
								vendor/github.com/abbot/go-http-auth/basic.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								vendor/github.com/abbot/go-http-auth/basic.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,163 @@ | |||
| package auth | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/sha1" | ||||
| 	"crypto/subtle" | ||||
| 	"encoding/base64" | ||||
| 	"errors" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"golang.org/x/crypto/bcrypt" | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
| 
 | ||||
| type compareFunc func(hashedPassword, password []byte) error | ||||
| 
 | ||||
| var ( | ||||
| 	errMismatchedHashAndPassword = errors.New("mismatched hash and password") | ||||
| 
 | ||||
| 	compareFuncs = []struct { | ||||
| 		prefix  string | ||||
| 		compare compareFunc | ||||
| 	}{ | ||||
| 		{"", compareMD5HashAndPassword}, // default compareFunc | ||||
| 		{"{SHA}", compareShaHashAndPassword}, | ||||
| 		// Bcrypt is complicated. According to crypt(3) from | ||||
| 		// crypt_blowfish version 1.3 (fetched from | ||||
| 		// http://www.openwall.com/crypt/crypt_blowfish-1.3.tar.gz), there | ||||
| 		// are three different has prefixes: "$2a$", used by versions up | ||||
| 		// to 1.0.4, and "$2x$" and "$2y$", used in all later | ||||
| 		// versions. "$2a$" has a known bug, "$2x$" was added as a | ||||
| 		// migration path for systems with "$2a$" prefix and still has a | ||||
| 		// bug, and only "$2y$" should be used by modern systems. The bug | ||||
| 		// has something to do with handling of 8-bit characters. Since | ||||
| 		// both "$2a$" and "$2x$" are deprecated, we are handling them the | ||||
| 		// same way as "$2y$", which will yield correct results for 7-bit | ||||
| 		// character passwords, but is wrong for 8-bit character | ||||
| 		// passwords. You have to upgrade to "$2y$" if you want sant 8-bit | ||||
| 		// character password support with bcrypt. To add to the mess, | ||||
| 		// OpenBSD 5.5. introduced "$2b$" prefix, which behaves exactly | ||||
| 		// like "$2y$" according to the same source. | ||||
| 		{"$2a$", bcrypt.CompareHashAndPassword}, | ||||
| 		{"$2b$", bcrypt.CompareHashAndPassword}, | ||||
| 		{"$2x$", bcrypt.CompareHashAndPassword}, | ||||
| 		{"$2y$", bcrypt.CompareHashAndPassword}, | ||||
| 	} | ||||
| ) | ||||
| 
 | ||||
| type BasicAuth struct { | ||||
| 	Realm   string | ||||
| 	Secrets SecretProvider | ||||
| 	// Headers used by authenticator. Set to ProxyHeaders to use with | ||||
| 	// proxy server. When nil, NormalHeaders are used. | ||||
| 	Headers *Headers | ||||
| } | ||||
| 
 | ||||
| // check that BasicAuth implements AuthenticatorInterface | ||||
| var _ = (AuthenticatorInterface)((*BasicAuth)(nil)) | ||||
| 
 | ||||
| /* | ||||
|  Checks the username/password combination from the request. Returns | ||||
|  either an empty string (authentication failed) or the name of the | ||||
|  authenticated user. | ||||
| 
 | ||||
|  Supports MD5 and SHA1 password entries | ||||
| */ | ||||
| func (a *BasicAuth) CheckAuth(r *http.Request) string { | ||||
| 	s := strings.SplitN(r.Header.Get(a.Headers.V().Authorization), " ", 2) | ||||
| 	if len(s) != 2 || s[0] != "Basic" { | ||||
| 		return "" | ||||
| 	} | ||||
| 
 | ||||
| 	b, err := base64.StdEncoding.DecodeString(s[1]) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	pair := strings.SplitN(string(b), ":", 2) | ||||
| 	if len(pair) != 2 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	user, password := pair[0], pair[1] | ||||
| 	secret := a.Secrets(user, a.Realm) | ||||
| 	if secret == "" { | ||||
| 		return "" | ||||
| 	} | ||||
| 	compare := compareFuncs[0].compare | ||||
| 	for _, cmp := range compareFuncs[1:] { | ||||
| 		if strings.HasPrefix(secret, cmp.prefix) { | ||||
| 			compare = cmp.compare | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if compare([]byte(secret), []byte(password)) != nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return pair[0] | ||||
| } | ||||
| 
 | ||||
| func compareShaHashAndPassword(hashedPassword, password []byte) error { | ||||
| 	d := sha1.New() | ||||
| 	d.Write(password) | ||||
| 	if subtle.ConstantTimeCompare(hashedPassword[5:], []byte(base64.StdEncoding.EncodeToString(d.Sum(nil)))) != 1 { | ||||
| 		return errMismatchedHashAndPassword | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func compareMD5HashAndPassword(hashedPassword, password []byte) error { | ||||
| 	parts := bytes.SplitN(hashedPassword, []byte("$"), 4) | ||||
| 	if len(parts) != 4 { | ||||
| 		return errMismatchedHashAndPassword | ||||
| 	} | ||||
| 	magic := []byte("$" + string(parts[1]) + "$") | ||||
| 	salt := parts[2] | ||||
| 	if subtle.ConstantTimeCompare(hashedPassword, MD5Crypt(password, salt, magic)) != 1 { | ||||
| 		return errMismatchedHashAndPassword | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  http.Handler for BasicAuth which initiates the authentication process | ||||
|  (or requires reauthentication). | ||||
| */ | ||||
| func (a *BasicAuth) RequireAuth(w http.ResponseWriter, r *http.Request) { | ||||
| 	w.Header().Set(contentType, a.Headers.V().UnauthContentType) | ||||
| 	w.Header().Set(a.Headers.V().Authenticate, `Basic realm="`+a.Realm+`"`) | ||||
| 	w.WriteHeader(a.Headers.V().UnauthCode) | ||||
| 	w.Write([]byte(a.Headers.V().UnauthResponse)) | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  BasicAuthenticator returns a function, which wraps an | ||||
|  AuthenticatedHandlerFunc converting it to http.HandlerFunc. This | ||||
|  wrapper function checks the authentication and either sends back | ||||
|  required authentication headers, or calls the wrapped function with | ||||
|  authenticated username in the AuthenticatedRequest. | ||||
| */ | ||||
| func (a *BasicAuth) Wrap(wrapped AuthenticatedHandlerFunc) http.HandlerFunc { | ||||
| 	return func(w http.ResponseWriter, r *http.Request) { | ||||
| 		if username := a.CheckAuth(r); username == "" { | ||||
| 			a.RequireAuth(w, r) | ||||
| 		} else { | ||||
| 			ar := &AuthenticatedRequest{Request: *r, Username: username} | ||||
| 			wrapped(w, ar) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // NewContext returns a context carrying authentication information for the request. | ||||
| func (a *BasicAuth) NewContext(ctx context.Context, r *http.Request) context.Context { | ||||
| 	info := &Info{Username: a.CheckAuth(r), ResponseHeaders: make(http.Header)} | ||||
| 	info.Authenticated = (info.Username != "") | ||||
| 	if !info.Authenticated { | ||||
| 		info.ResponseHeaders.Set(a.Headers.V().Authenticate, `Basic realm="`+a.Realm+`"`) | ||||
| 	} | ||||
| 	return context.WithValue(ctx, infoKey, info) | ||||
| } | ||||
| 
 | ||||
| func NewBasicAuthenticator(realm string, secrets SecretProvider) *BasicAuth { | ||||
| 	return &BasicAuth{Realm: realm, Secrets: secrets} | ||||
| } | ||||
							
								
								
									
										40
									
								
								vendor/github.com/abbot/go-http-auth/basic_test.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								vendor/github.com/abbot/go-http-auth/basic_test.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | |||
| package auth | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"net/http" | ||||
| 	"testing" | ||||
| ) | ||||
| 
 | ||||
| func TestAuthBasic(t *testing.T) { | ||||
| 	secrets := HtpasswdFileProvider("test.htpasswd") | ||||
| 	a := &BasicAuth{Realm: "example.com", Secrets: secrets} | ||||
| 	r := &http.Request{} | ||||
| 	r.Method = "GET" | ||||
| 	if a.CheckAuth(r) != "" { | ||||
| 		t.Fatal("CheckAuth passed on empty headers") | ||||
| 	} | ||||
| 	r.Header = http.Header(make(map[string][]string)) | ||||
| 	r.Header.Set("Authorization", "Digest blabla ololo") | ||||
| 	if a.CheckAuth(r) != "" { | ||||
| 		t.Fatal("CheckAuth passed on bad headers") | ||||
| 	} | ||||
| 	r.Header.Set("Authorization", "Basic !@#") | ||||
| 	if a.CheckAuth(r) != "" { | ||||
| 		t.Fatal("CheckAuth passed on bad base64 data") | ||||
| 	} | ||||
| 
 | ||||
| 	data := [][]string{ | ||||
| 		{"test", "hello"}, | ||||
| 		{"test2", "hello2"}, | ||||
| 		{"test3", "hello3"}, | ||||
| 		{"test16", "topsecret"}, | ||||
| 	} | ||||
| 	for _, tc := range data { | ||||
| 		auth := base64.StdEncoding.EncodeToString([]byte(tc[0] + ":" + tc[1])) | ||||
| 		r.Header.Set("Authorization", "Basic "+auth) | ||||
| 		if a.CheckAuth(r) != tc[0] { | ||||
| 			t.Fatalf("CheckAuth failed for user '%s'", tc[0]) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										274
									
								
								vendor/github.com/abbot/go-http-auth/digest.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								vendor/github.com/abbot/go-http-auth/digest.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,274 @@ | |||
| package auth | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/subtle" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
| 
 | ||||
| type digest_client struct { | ||||
| 	nc        uint64 | ||||
| 	last_seen int64 | ||||
| } | ||||
| 
 | ||||
| type DigestAuth struct { | ||||
| 	Realm            string | ||||
| 	Opaque           string | ||||
| 	Secrets          SecretProvider | ||||
| 	PlainTextSecrets bool | ||||
| 	IgnoreNonceCount bool | ||||
| 	// Headers used by authenticator. Set to ProxyHeaders to use with | ||||
| 	// proxy server. When nil, NormalHeaders are used. | ||||
| 	Headers *Headers | ||||
| 
 | ||||
| 	/* | ||||
| 	   Approximate size of Client's Cache. When actual number of | ||||
| 	   tracked client nonces exceeds | ||||
| 	   ClientCacheSize+ClientCacheTolerance, ClientCacheTolerance*2 | ||||
| 	   older entries are purged. | ||||
| 	*/ | ||||
| 	ClientCacheSize      int | ||||
| 	ClientCacheTolerance int | ||||
| 
 | ||||
| 	clients map[string]*digest_client | ||||
| 	mutex   sync.Mutex | ||||
| } | ||||
| 
 | ||||
| // check that DigestAuth implements AuthenticatorInterface | ||||
| var _ = (AuthenticatorInterface)((*DigestAuth)(nil)) | ||||
| 
 | ||||
| type digest_cache_entry struct { | ||||
| 	nonce     string | ||||
| 	last_seen int64 | ||||
| } | ||||
| 
 | ||||
| type digest_cache []digest_cache_entry | ||||
| 
 | ||||
| func (c digest_cache) Less(i, j int) bool { | ||||
| 	return c[i].last_seen < c[j].last_seen | ||||
| } | ||||
| 
 | ||||
| func (c digest_cache) Len() int { | ||||
| 	return len(c) | ||||
| } | ||||
| 
 | ||||
| func (c digest_cache) Swap(i, j int) { | ||||
| 	c[i], c[j] = c[j], c[i] | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  Remove count oldest entries from DigestAuth.clients | ||||
| */ | ||||
| func (a *DigestAuth) Purge(count int) { | ||||
| 	entries := make([]digest_cache_entry, 0, len(a.clients)) | ||||
| 	for nonce, client := range a.clients { | ||||
| 		entries = append(entries, digest_cache_entry{nonce, client.last_seen}) | ||||
| 	} | ||||
| 	cache := digest_cache(entries) | ||||
| 	sort.Sort(cache) | ||||
| 	for _, client := range cache[:count] { | ||||
| 		delete(a.clients, client.nonce) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  http.Handler for DigestAuth which initiates the authentication process | ||||
|  (or requires reauthentication). | ||||
| */ | ||||
| func (a *DigestAuth) RequireAuth(w http.ResponseWriter, r *http.Request) { | ||||
| 	if len(a.clients) > a.ClientCacheSize+a.ClientCacheTolerance { | ||||
| 		a.Purge(a.ClientCacheTolerance * 2) | ||||
| 	} | ||||
| 	nonce := RandomKey() | ||||
| 	a.clients[nonce] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()} | ||||
| 	w.Header().Set(contentType, a.Headers.V().UnauthContentType) | ||||
| 	w.Header().Set(a.Headers.V().Authenticate, | ||||
| 		fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm="MD5", qop="auth"`, | ||||
| 			a.Realm, nonce, a.Opaque)) | ||||
| 	w.WriteHeader(a.Headers.V().UnauthCode) | ||||
| 	w.Write([]byte(a.Headers.V().UnauthResponse)) | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  Parse Authorization header from the http.Request. Returns a map of | ||||
|  auth parameters or nil if the header is not a valid parsable Digest | ||||
|  auth header. | ||||
| */ | ||||
| func DigestAuthParams(authorization string) map[string]string { | ||||
| 	s := strings.SplitN(authorization, " ", 2) | ||||
| 	if len(s) != 2 || s[0] != "Digest" { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	return ParsePairs(s[1]) | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  Check if request contains valid authentication data. Returns a pair | ||||
|  of username, authinfo where username is the name of the authenticated | ||||
|  user or an empty string and authinfo is the contents for the optional | ||||
|  Authentication-Info response header. | ||||
| */ | ||||
| func (da *DigestAuth) CheckAuth(r *http.Request) (username string, authinfo *string) { | ||||
| 	da.mutex.Lock() | ||||
| 	defer da.mutex.Unlock() | ||||
| 	username = "" | ||||
| 	authinfo = nil | ||||
| 	auth := DigestAuthParams(r.Header.Get(da.Headers.V().Authorization)) | ||||
| 	if auth == nil { | ||||
| 		return "", nil | ||||
| 	} | ||||
| 	// RFC2617 Section 3.2.1 specifies that unset value of algorithm in | ||||
| 	// WWW-Authenticate Response header should be treated as | ||||
| 	// "MD5". According to section 3.2.2 the "algorithm" value in | ||||
| 	// subsequent Request Authorization header must be set to whatever | ||||
| 	// was supplied in the WWW-Authenticate Response header. This | ||||
| 	// implementation always returns an algorithm in WWW-Authenticate | ||||
| 	// header, however there seems to be broken clients in the wild | ||||
| 	// which do not set the algorithm. Assume the unset algorithm in | ||||
| 	// Authorization header to be equal to MD5. | ||||
| 	if _, ok := auth["algorithm"]; !ok { | ||||
| 		auth["algorithm"] = "MD5" | ||||
| 	} | ||||
| 	if da.Opaque != auth["opaque"] || auth["algorithm"] != "MD5" || auth["qop"] != "auth" { | ||||
| 		return "", nil | ||||
| 	} | ||||
| 
 | ||||
| 	// Check if the requested URI matches auth header | ||||
| 	if r.RequestURI != auth["uri"] { | ||||
| 		// We allow auth["uri"] to be a full path prefix of request-uri | ||||
| 		// for some reason lost in history, which is probably wrong, but | ||||
| 		// used to be like that for quite some time | ||||
| 		// (https://tools.ietf.org/html/rfc2617#section-3.2.2 explicitly | ||||
| 		// says that auth["uri"] is the request-uri). | ||||
| 		// | ||||
| 		// TODO: make an option to allow only strict checking. | ||||
| 		switch u, err := url.Parse(auth["uri"]); { | ||||
| 		case err != nil: | ||||
| 			return "", nil | ||||
| 		case r.URL == nil: | ||||
| 			return "", nil | ||||
| 		case len(u.Path) > len(r.URL.Path): | ||||
| 			return "", nil | ||||
| 		case !strings.HasPrefix(r.URL.Path, u.Path): | ||||
| 			return "", nil | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	HA1 := da.Secrets(auth["username"], da.Realm) | ||||
| 	if da.PlainTextSecrets { | ||||
| 		HA1 = H(auth["username"] + ":" + da.Realm + ":" + HA1) | ||||
| 	} | ||||
| 	HA2 := H(r.Method + ":" + auth["uri"]) | ||||
| 	KD := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], HA2}, ":")) | ||||
| 
 | ||||
| 	if subtle.ConstantTimeCompare([]byte(KD), []byte(auth["response"])) != 1 { | ||||
| 		return "", nil | ||||
| 	} | ||||
| 
 | ||||
| 	// At this point crypto checks are completed and validated. | ||||
| 	// Now check if the session is valid. | ||||
| 
 | ||||
| 	nc, err := strconv.ParseUint(auth["nc"], 16, 64) | ||||
| 	if err != nil { | ||||
| 		return "", nil | ||||
| 	} | ||||
| 
 | ||||
| 	if client, ok := da.clients[auth["nonce"]]; !ok { | ||||
| 		return "", nil | ||||
| 	} else { | ||||
| 		if client.nc != 0 && client.nc >= nc && !da.IgnoreNonceCount { | ||||
| 			return "", nil | ||||
| 		} | ||||
| 		client.nc = nc | ||||
| 		client.last_seen = time.Now().UnixNano() | ||||
| 	} | ||||
| 
 | ||||
| 	resp_HA2 := H(":" + auth["uri"]) | ||||
| 	rspauth := H(strings.Join([]string{HA1, auth["nonce"], auth["nc"], auth["cnonce"], auth["qop"], resp_HA2}, ":")) | ||||
| 
 | ||||
| 	info := fmt.Sprintf(`qop="auth", rspauth="%s", cnonce="%s", nc="%s"`, rspauth, auth["cnonce"], auth["nc"]) | ||||
| 	return auth["username"], &info | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  Default values for ClientCacheSize and ClientCacheTolerance for DigestAuth | ||||
| */ | ||||
| const DefaultClientCacheSize = 1000 | ||||
| const DefaultClientCacheTolerance = 100 | ||||
| 
 | ||||
| /* | ||||
|  Wrap returns an Authenticator which uses HTTP Digest | ||||
|  authentication. Arguments: | ||||
| 
 | ||||
|  realm: The authentication realm. | ||||
| 
 | ||||
|  secrets: SecretProvider which must return HA1 digests for the same | ||||
|  realm as above. | ||||
| */ | ||||
| func (a *DigestAuth) Wrap(wrapped AuthenticatedHandlerFunc) http.HandlerFunc { | ||||
| 	return func(w http.ResponseWriter, r *http.Request) { | ||||
| 		if username, authinfo := a.CheckAuth(r); username == "" { | ||||
| 			a.RequireAuth(w, r) | ||||
| 		} else { | ||||
| 			ar := &AuthenticatedRequest{Request: *r, Username: username} | ||||
| 			if authinfo != nil { | ||||
| 				w.Header().Set(a.Headers.V().AuthInfo, *authinfo) | ||||
| 			} | ||||
| 			wrapped(w, ar) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  JustCheck returns function which converts an http.HandlerFunc into a | ||||
|  http.HandlerFunc which requires authentication. Username is passed as | ||||
|  an extra X-Authenticated-Username header. | ||||
| */ | ||||
| func (a *DigestAuth) JustCheck(wrapped http.HandlerFunc) http.HandlerFunc { | ||||
| 	return a.Wrap(func(w http.ResponseWriter, ar *AuthenticatedRequest) { | ||||
| 		ar.Header.Set(AuthUsernameHeader, ar.Username) | ||||
| 		wrapped(w, &ar.Request) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // NewContext returns a context carrying authentication information for the request. | ||||
| func (a *DigestAuth) NewContext(ctx context.Context, r *http.Request) context.Context { | ||||
| 	username, authinfo := a.CheckAuth(r) | ||||
| 	info := &Info{Username: username, ResponseHeaders: make(http.Header)} | ||||
| 	if username != "" { | ||||
| 		info.Authenticated = true | ||||
| 		info.ResponseHeaders.Set(a.Headers.V().AuthInfo, *authinfo) | ||||
| 	} else { | ||||
| 		// return back digest WWW-Authenticate header | ||||
| 		if len(a.clients) > a.ClientCacheSize+a.ClientCacheTolerance { | ||||
| 			a.Purge(a.ClientCacheTolerance * 2) | ||||
| 		} | ||||
| 		nonce := RandomKey() | ||||
| 		a.clients[nonce] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()} | ||||
| 		info.ResponseHeaders.Set(a.Headers.V().Authenticate, | ||||
| 			fmt.Sprintf(`Digest realm="%s", nonce="%s", opaque="%s", algorithm="MD5", qop="auth"`, | ||||
| 				a.Realm, nonce, a.Opaque)) | ||||
| 	} | ||||
| 	return context.WithValue(ctx, infoKey, info) | ||||
| } | ||||
| 
 | ||||
| func NewDigestAuthenticator(realm string, secrets SecretProvider) *DigestAuth { | ||||
| 	da := &DigestAuth{ | ||||
| 		Opaque:               RandomKey(), | ||||
| 		Realm:                realm, | ||||
| 		Secrets:              secrets, | ||||
| 		PlainTextSecrets:     false, | ||||
| 		ClientCacheSize:      DefaultClientCacheSize, | ||||
| 		ClientCacheTolerance: DefaultClientCacheTolerance, | ||||
| 		clients:              map[string]*digest_client{}} | ||||
| 	return da | ||||
| } | ||||
							
								
								
									
										76
									
								
								vendor/github.com/abbot/go-http-auth/digest_test.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								vendor/github.com/abbot/go-http-auth/digest_test.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,76 @@ | |||
| package auth | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| func TestAuthDigest(t *testing.T) { | ||||
| 	secrets := HtdigestFileProvider("test.htdigest") | ||||
| 	da := &DigestAuth{Opaque: "U7H+ier3Ae8Skd/g", | ||||
| 		Realm:   "example.com", | ||||
| 		Secrets: secrets, | ||||
| 		clients: map[string]*digest_client{}} | ||||
| 	r := &http.Request{} | ||||
| 	r.Method = "GET" | ||||
| 	if u, _ := da.CheckAuth(r); u != "" { | ||||
| 		t.Fatal("non-empty auth for empty request header") | ||||
| 	} | ||||
| 	r.Header = http.Header(make(map[string][]string)) | ||||
| 	r.Header.Set("Authorization", "Digest blabla") | ||||
| 	if u, _ := da.CheckAuth(r); u != "" { | ||||
| 		t.Fatal("non-empty auth for bad request header") | ||||
| 	} | ||||
| 	r.Header.Set("Authorization", `Digest username="test", realm="example.com", nonce="Vb9BP/h81n3GpTTB", uri="/t2", cnonce="NjE4MTM2", nc=00000001, qop="auth", response="ffc357c4eba74773c8687e0bc724c9a3", opaque="U7H+ier3Ae8Skd/g", algorithm="MD5"`) | ||||
| 	if u, _ := da.CheckAuth(r); u != "" { | ||||
| 		t.Fatal("non-empty auth for unknown client") | ||||
| 	} | ||||
| 
 | ||||
| 	r.URL, _ = url.Parse("/t2") | ||||
| 	da.clients["Vb9BP/h81n3GpTTB"] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()} | ||||
| 	if u, _ := da.CheckAuth(r); u != "test" { | ||||
| 		t.Fatal("empty auth for legitimate client") | ||||
| 	} | ||||
| 
 | ||||
| 	// our nc is now 0, client nc is 1 | ||||
| 	if u, _ := da.CheckAuth(r); u != "" { | ||||
| 		t.Fatal("non-empty auth for outdated nc") | ||||
| 	} | ||||
| 
 | ||||
| 	// try again with nc checking off | ||||
| 	da.IgnoreNonceCount = true | ||||
| 	if u, _ := da.CheckAuth(r); u != "test" { | ||||
| 		t.Fatal("empty auth for outdated nc even though nc checking is off") | ||||
| 	} | ||||
| 	da.IgnoreNonceCount = false | ||||
| 
 | ||||
| 	r.URL, _ = url.Parse("/") | ||||
| 	da.clients["Vb9BP/h81n3GpTTB"] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()} | ||||
| 	if u, _ := da.CheckAuth(r); u != "" { | ||||
| 		t.Fatal("non-empty auth for bad request path") | ||||
| 	} | ||||
| 
 | ||||
| 	r.URL, _ = url.Parse("/t3") | ||||
| 	da.clients["Vb9BP/h81n3GpTTB"] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()} | ||||
| 	if u, _ := da.CheckAuth(r); u != "" { | ||||
| 		t.Fatal("non-empty auth for bad request path") | ||||
| 	} | ||||
| 
 | ||||
| 	da.clients["+RbVXSbIoa1SaJk1"] = &digest_client{nc: 0, last_seen: time.Now().UnixNano()} | ||||
| 	r.Header.Set("Authorization", `Digest username="test", realm="example.com", nonce="+RbVXSbIoa1SaJk1", uri="/", cnonce="NjE4NDkw", nc=00000001, qop="auth", response="c08918024d7faaabd5424654c4e3ad1c", opaque="U7H+ier3Ae8Skd/g", algorithm="MD5"`) | ||||
| 	if u, _ := da.CheckAuth(r); u != "test" { | ||||
| 		t.Fatal("empty auth for valid request in subpath") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestDigestAuthParams(t *testing.T) { | ||||
| 	const authorization = `Digest username="test", realm="", nonce="FRPnGdb8lvM1UHhi", uri="/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro", algorithm=MD5, response="fdcdd78e5b306ffed343d0ec3967f2e5", opaque="lEgVjogmIar2fg/t", qop=auth, nc=00000001, cnonce="e76b05db27a3b323"` | ||||
| 
 | ||||
| 	params := DigestAuthParams(authorization) | ||||
| 	want := "/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro" | ||||
| 	if params["uri"] != want { | ||||
| 		t.Fatalf("failed to parse uri with embedded commas, got %q want %q", params["uri"], want) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										35
									
								
								vendor/github.com/abbot/go-http-auth/examples/basic.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								vendor/github.com/abbot/go-http-auth/examples/basic.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | |||
| // +build ignore | ||||
| 
 | ||||
| /* | ||||
|  Example application using Basic auth | ||||
| 
 | ||||
|  Build with: | ||||
| 
 | ||||
|  go build basic.go | ||||
| */ | ||||
| 
 | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	auth ".." | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| ) | ||||
| 
 | ||||
| func Secret(user, realm string) string { | ||||
| 	if user == "john" { | ||||
| 		// password is "hello" | ||||
| 		return "$1$dlPL2MqE$oQmn16q49SqdmhenQuNgs1" | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| func handle(w http.ResponseWriter, r *auth.AuthenticatedRequest) { | ||||
| 	fmt.Fprintf(w, "<html><body><h1>Hello, %s!</h1></body></html>", r.Username) | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
| 	authenticator := auth.NewBasicAuthenticator("example.com", Secret) | ||||
| 	http.HandleFunc("/", authenticator.Wrap(handle)) | ||||
| 	http.ListenAndServe(":8080", nil) | ||||
| } | ||||
							
								
								
									
										60
									
								
								vendor/github.com/abbot/go-http-auth/examples/context.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								vendor/github.com/abbot/go-http-auth/examples/context.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | |||
| // +build ignore | ||||
| 
 | ||||
| /* | ||||
|  Example application using NewContext/FromContext | ||||
| 
 | ||||
|  Build with: | ||||
| 
 | ||||
|  go build context.go | ||||
| */ | ||||
| 
 | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 
 | ||||
| 	auth ".." | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
| 
 | ||||
| func Secret(user, realm string) string { | ||||
| 	if user == "john" { | ||||
| 		// password is "hello" | ||||
| 		return "b98e16cbc3d01734b264adba7baa3bf9" | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| type ContextHandler interface { | ||||
| 	ServeHTTP(ctx context.Context, w http.ResponseWriter, r *http.Request) | ||||
| } | ||||
| 
 | ||||
| type ContextHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request) | ||||
| 
 | ||||
| func (f ContextHandlerFunc) ServeHTTP(ctx context.Context, w http.ResponseWriter, r *http.Request) { | ||||
| 	f(ctx, w, r) | ||||
| } | ||||
| 
 | ||||
| func handle(ctx context.Context, w http.ResponseWriter, r *http.Request) { | ||||
| 	authInfo := auth.FromContext(ctx) | ||||
| 	authInfo.UpdateHeaders(w.Header()) | ||||
| 	if authInfo == nil || !authInfo.Authenticated { | ||||
| 		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) | ||||
| 		return | ||||
| 	} | ||||
| 	fmt.Fprintf(w, "<html><body><h1>Hello, %s!</h1></body></html>", authInfo.Username) | ||||
| } | ||||
| 
 | ||||
| func authenticatedHandler(a auth.AuthenticatorInterface, h ContextHandler) http.Handler { | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		ctx := a.NewContext(context.Background(), r) | ||||
| 		h.ServeHTTP(ctx, w, r) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
| 	authenticator := auth.NewDigestAuthenticator("example.com", Secret) | ||||
| 	http.Handle("/", authenticatedHandler(authenticator, ContextHandlerFunc(handle))) | ||||
| 	http.ListenAndServe(":8080", nil) | ||||
| } | ||||
							
								
								
									
										35
									
								
								vendor/github.com/abbot/go-http-auth/examples/digest.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								vendor/github.com/abbot/go-http-auth/examples/digest.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | |||
| // +build ignore | ||||
| 
 | ||||
| /* | ||||
|  Example application using Digest auth | ||||
| 
 | ||||
|  Build with: | ||||
| 
 | ||||
|  go build digest.go | ||||
| */ | ||||
| 
 | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	auth ".." | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| ) | ||||
| 
 | ||||
| func Secret(user, realm string) string { | ||||
| 	if user == "john" { | ||||
| 		// password is "hello" | ||||
| 		return "b98e16cbc3d01734b264adba7baa3bf9" | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| func handle(w http.ResponseWriter, r *auth.AuthenticatedRequest) { | ||||
| 	fmt.Fprintf(w, "<html><body><h1>Hello, %s!</h1></body></html>", r.Username) | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
| 	authenticator := auth.NewDigestAuthenticator("example.com", Secret) | ||||
| 	http.HandleFunc("/", authenticator.Wrap(handle)) | ||||
| 	http.ListenAndServe(":8080", nil) | ||||
| } | ||||
							
								
								
									
										36
									
								
								vendor/github.com/abbot/go-http-auth/examples/wrapped.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								vendor/github.com/abbot/go-http-auth/examples/wrapped.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | |||
| // +build ignore | ||||
| 
 | ||||
| /* | ||||
|  Example demonstrating how to wrap an application which is unaware of | ||||
|  authenticated requests with a "pass-through" authentication | ||||
| 
 | ||||
|  Build with: | ||||
| 
 | ||||
|  go build wrapped.go | ||||
| */ | ||||
| 
 | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	auth ".." | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| ) | ||||
| 
 | ||||
| func Secret(user, realm string) string { | ||||
| 	if user == "john" { | ||||
| 		// password is "hello" | ||||
| 		return "$1$dlPL2MqE$oQmn16q49SqdmhenQuNgs1" | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| func regular_handler(w http.ResponseWriter, r *http.Request) { | ||||
| 	fmt.Fprintf(w, "<html><body><h1>This application is unaware of authentication</h1></body></html>") | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
| 	authenticator := auth.NewBasicAuthenticator("example.com", Secret) | ||||
| 	http.HandleFunc("/", auth.JustCheck(authenticator, regular_handler)) | ||||
| 	http.ListenAndServe(":8080", nil) | ||||
| } | ||||
							
								
								
									
										92
									
								
								vendor/github.com/abbot/go-http-auth/md5crypt.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								vendor/github.com/abbot/go-http-auth/md5crypt.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,92 @@ | |||
| package auth | ||||
| 
 | ||||
| import "crypto/md5" | ||||
| import "strings" | ||||
| 
 | ||||
| const itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | ||||
| 
 | ||||
| var md5_crypt_swaps = [16]int{12, 6, 0, 13, 7, 1, 14, 8, 2, 15, 9, 3, 5, 10, 4, 11} | ||||
| 
 | ||||
| type MD5Entry struct { | ||||
| 	Magic, Salt, Hash []byte | ||||
| } | ||||
| 
 | ||||
| func NewMD5Entry(e string) *MD5Entry { | ||||
| 	parts := strings.SplitN(e, "$", 4) | ||||
| 	if len(parts) != 4 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return &MD5Entry{ | ||||
| 		Magic: []byte("$" + parts[1] + "$"), | ||||
| 		Salt:  []byte(parts[2]), | ||||
| 		Hash:  []byte(parts[3]), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  MD5 password crypt implementation | ||||
| */ | ||||
| func MD5Crypt(password, salt, magic []byte) []byte { | ||||
| 	d := md5.New() | ||||
| 
 | ||||
| 	d.Write(password) | ||||
| 	d.Write(magic) | ||||
| 	d.Write(salt) | ||||
| 
 | ||||
| 	d2 := md5.New() | ||||
| 	d2.Write(password) | ||||
| 	d2.Write(salt) | ||||
| 	d2.Write(password) | ||||
| 
 | ||||
| 	for i, mixin := 0, d2.Sum(nil); i < len(password); i++ { | ||||
| 		d.Write([]byte{mixin[i%16]}) | ||||
| 	} | ||||
| 
 | ||||
| 	for i := len(password); i != 0; i >>= 1 { | ||||
| 		if i&1 == 0 { | ||||
| 			d.Write([]byte{password[0]}) | ||||
| 		} else { | ||||
| 			d.Write([]byte{0}) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	final := d.Sum(nil) | ||||
| 
 | ||||
| 	for i := 0; i < 1000; i++ { | ||||
| 		d2 := md5.New() | ||||
| 		if i&1 == 0 { | ||||
| 			d2.Write(final) | ||||
| 		} else { | ||||
| 			d2.Write(password) | ||||
| 		} | ||||
| 
 | ||||
| 		if i%3 != 0 { | ||||
| 			d2.Write(salt) | ||||
| 		} | ||||
| 
 | ||||
| 		if i%7 != 0 { | ||||
| 			d2.Write(password) | ||||
| 		} | ||||
| 
 | ||||
| 		if i&1 == 0 { | ||||
| 			d2.Write(password) | ||||
| 		} else { | ||||
| 			d2.Write(final) | ||||
| 		} | ||||
| 		final = d2.Sum(nil) | ||||
| 	} | ||||
| 
 | ||||
| 	result := make([]byte, 0, 22) | ||||
| 	v := uint(0) | ||||
| 	bits := uint(0) | ||||
| 	for _, i := range md5_crypt_swaps { | ||||
| 		v |= (uint(final[i]) << bits) | ||||
| 		for bits = bits + 8; bits > 6; bits -= 6 { | ||||
| 			result = append(result, itoa64[v&0x3f]) | ||||
| 			v >>= 6 | ||||
| 		} | ||||
| 	} | ||||
| 	result = append(result, itoa64[v&0x3f]) | ||||
| 
 | ||||
| 	return append(append(append(magic, salt...), '$'), result...) | ||||
| } | ||||
							
								
								
									
										19
									
								
								vendor/github.com/abbot/go-http-auth/md5crypt_test.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/abbot/go-http-auth/md5crypt_test.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| package auth | ||||
| 
 | ||||
| import "testing" | ||||
| 
 | ||||
| func Test_MD5Crypt(t *testing.T) { | ||||
| 	test_cases := [][]string{ | ||||
| 		{"apache", "$apr1$J.w5a/..$IW9y6DR0oO/ADuhlMF5/X1"}, | ||||
| 		{"pass", "$1$YeNsbWdH$wvOF8JdqsoiLix754LTW90"}, | ||||
| 		{"topsecret", "$apr1$JI4wh3am$AmhephVqLTUyAVpFQeHZC0"}, | ||||
| 	} | ||||
| 	for _, tc := range test_cases { | ||||
| 		e := NewMD5Entry(tc[1]) | ||||
| 		result := MD5Crypt([]byte(tc[0]), e.Salt, e.Magic) | ||||
| 		if string(result) != tc[1] { | ||||
| 			t.Fatalf("MD5Crypt returned '%s' instead of '%s'", string(result), tc[1]) | ||||
| 		} | ||||
| 		t.Logf("MD5Crypt: '%s' (%s%s$) -> %s", tc[0], e.Magic, e.Salt, result) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										141
									
								
								vendor/github.com/abbot/go-http-auth/misc.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								vendor/github.com/abbot/go-http-auth/misc.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,141 @@ | |||
| package auth | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/md5" | ||||
| 	"crypto/rand" | ||||
| 	"encoding/base64" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // RandomKey returns a random 16-byte base64 alphabet string | ||||
| func RandomKey() string { | ||||
| 	k := make([]byte, 12) | ||||
| 	for bytes := 0; bytes < len(k); { | ||||
| 		n, err := rand.Read(k[bytes:]) | ||||
| 		if err != nil { | ||||
| 			panic("rand.Read() failed") | ||||
| 		} | ||||
| 		bytes += n | ||||
| 	} | ||||
| 	return base64.StdEncoding.EncodeToString(k) | ||||
| } | ||||
| 
 | ||||
| // H function for MD5 algorithm (returns a lower-case hex MD5 digest) | ||||
| func H(data string) string { | ||||
| 	digest := md5.New() | ||||
| 	digest.Write([]byte(data)) | ||||
| 	return fmt.Sprintf("%x", digest.Sum(nil)) | ||||
| } | ||||
| 
 | ||||
| // ParseList parses a comma-separated list of values as described by | ||||
| // RFC 2068 and returns list elements. | ||||
| // | ||||
| // Lifted from https://code.google.com/p/gorilla/source/browse/http/parser/parser.go | ||||
| // which was ported from urllib2.parse_http_list, from the Python | ||||
| // standard library. | ||||
| func ParseList(value string) []string { | ||||
| 	var list []string | ||||
| 	var escape, quote bool | ||||
| 	b := new(bytes.Buffer) | ||||
| 	for _, r := range value { | ||||
| 		switch { | ||||
| 		case escape: | ||||
| 			b.WriteRune(r) | ||||
| 			escape = false | ||||
| 		case quote: | ||||
| 			if r == '\\' { | ||||
| 				escape = true | ||||
| 			} else { | ||||
| 				if r == '"' { | ||||
| 					quote = false | ||||
| 				} | ||||
| 				b.WriteRune(r) | ||||
| 			} | ||||
| 		case r == ',': | ||||
| 			list = append(list, strings.TrimSpace(b.String())) | ||||
| 			b.Reset() | ||||
| 		case r == '"': | ||||
| 			quote = true | ||||
| 			b.WriteRune(r) | ||||
| 		default: | ||||
| 			b.WriteRune(r) | ||||
| 		} | ||||
| 	} | ||||
| 	// Append last part. | ||||
| 	if s := b.String(); s != "" { | ||||
| 		list = append(list, strings.TrimSpace(s)) | ||||
| 	} | ||||
| 	return list | ||||
| } | ||||
| 
 | ||||
| // ParsePairs extracts key/value pairs from a comma-separated list of | ||||
| // values as described by RFC 2068 and returns a map[key]value. The | ||||
| // resulting values are unquoted. If a list element doesn't contain a | ||||
| // "=", the key is the element itself and the value is an empty | ||||
| // string. | ||||
| // | ||||
| // Lifted from https://code.google.com/p/gorilla/source/browse/http/parser/parser.go | ||||
| func ParsePairs(value string) map[string]string { | ||||
| 	m := make(map[string]string) | ||||
| 	for _, pair := range ParseList(strings.TrimSpace(value)) { | ||||
| 		if i := strings.Index(pair, "="); i < 0 { | ||||
| 			m[pair] = "" | ||||
| 		} else { | ||||
| 			v := pair[i+1:] | ||||
| 			if v[0] == '"' && v[len(v)-1] == '"' { | ||||
| 				// Unquote it. | ||||
| 				v = v[1 : len(v)-1] | ||||
| 			} | ||||
| 			m[pair[:i]] = v | ||||
| 		} | ||||
| 	} | ||||
| 	return m | ||||
| } | ||||
| 
 | ||||
| // Headers contains header and error codes used by authenticator. | ||||
| type Headers struct { | ||||
| 	Authenticate      string // WWW-Authenticate | ||||
| 	Authorization     string // Authorization | ||||
| 	AuthInfo          string // Authentication-Info | ||||
| 	UnauthCode        int    // 401 | ||||
| 	UnauthContentType string // text/plain | ||||
| 	UnauthResponse    string // Unauthorized. | ||||
| } | ||||
| 
 | ||||
| // V returns NormalHeaders when h is nil, or h otherwise. Allows to | ||||
| // use uninitialized *Headers values in structs. | ||||
| func (h *Headers) V() *Headers { | ||||
| 	if h == nil { | ||||
| 		return NormalHeaders | ||||
| 	} | ||||
| 	return h | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	// NormalHeaders are the regular Headers used by an HTTP Server for | ||||
| 	// request authentication. | ||||
| 	NormalHeaders = &Headers{ | ||||
| 		Authenticate:      "WWW-Authenticate", | ||||
| 		Authorization:     "Authorization", | ||||
| 		AuthInfo:          "Authentication-Info", | ||||
| 		UnauthCode:        http.StatusUnauthorized, | ||||
| 		UnauthContentType: "text/plain", | ||||
| 		UnauthResponse:    fmt.Sprintf("%d %s\n", http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized)), | ||||
| 	} | ||||
| 
 | ||||
| 	// ProxyHeaders are Headers used by an HTTP Proxy server for proxy | ||||
| 	// access authentication. | ||||
| 	ProxyHeaders = &Headers{ | ||||
| 		Authenticate:      "Proxy-Authenticate", | ||||
| 		Authorization:     "Proxy-Authorization", | ||||
| 		AuthInfo:          "Proxy-Authentication-Info", | ||||
| 		UnauthCode:        http.StatusProxyAuthRequired, | ||||
| 		UnauthContentType: "text/plain", | ||||
| 		UnauthResponse:    fmt.Sprintf("%d %s\n", http.StatusProxyAuthRequired, http.StatusText(http.StatusProxyAuthRequired)), | ||||
| 	} | ||||
| ) | ||||
| 
 | ||||
| const contentType = "Content-Type" | ||||
							
								
								
									
										37
									
								
								vendor/github.com/abbot/go-http-auth/misc_test.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								vendor/github.com/abbot/go-http-auth/misc_test.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | |||
| package auth | ||||
| 
 | ||||
| import ( | ||||
| 	"reflect" | ||||
| 	"testing" | ||||
| ) | ||||
| 
 | ||||
| func TestH(t *testing.T) { | ||||
| 	const hello = "Hello, world!" | ||||
| 	const hello_md5 = "6cd3556deb0da54bca060b4c39479839" | ||||
| 	h := H(hello) | ||||
| 	if h != hello_md5 { | ||||
| 		t.Fatal("Incorrect digest for test string:", h, "instead of", hello_md5) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestParsePairs(t *testing.T) { | ||||
| 	const header = `username="\test", realm="a \"quoted\" string", nonce="FRPnGdb8lvM1UHhi", uri="/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro", algorithm=MD5, response="fdcdd78e5b306ffed343d0ec3967f2e5", opaque="lEgVjogmIar2fg/t", qop=auth, nc=00000001, cnonce="e76b05db27a3b323"` | ||||
| 
 | ||||
| 	want := map[string]string{ | ||||
| 		"username":  "test", | ||||
| 		"realm":     `a "quoted" string`, | ||||
| 		"nonce":     "FRPnGdb8lvM1UHhi", | ||||
| 		"uri":       "/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro", | ||||
| 		"algorithm": "MD5", | ||||
| 		"response":  "fdcdd78e5b306ffed343d0ec3967f2e5", | ||||
| 		"opaque":    "lEgVjogmIar2fg/t", | ||||
| 		"qop":       "auth", | ||||
| 		"nc":        "00000001", | ||||
| 		"cnonce":    "e76b05db27a3b323", | ||||
| 	} | ||||
| 	got := ParsePairs(header) | ||||
| 
 | ||||
| 	if !reflect.DeepEqual(got, want) { | ||||
| 		t.Fatalf("failed to correctly parse pairs, got %v, want %v\ndiff: %s", got, want) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										1
									
								
								vendor/github.com/abbot/go-http-auth/test.htdigest
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/abbot/go-http-auth/test.htdigest
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| test:example.com:aa78524fceb0e50fd8ca96dd818b8cf9 | ||||
							
								
								
									
										4
									
								
								vendor/github.com/abbot/go-http-auth/test.htpasswd
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/abbot/go-http-auth/test.htpasswd
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | |||
| test:{SHA}qvTGHdzF6KLavt4PO0gs2a6pQ00= | ||||
| test2:$apr1$a0j62R97$mYqFkloXH0/UOaUnAiV2b0 | ||||
| test16:$apr1$JI4wh3am$AmhephVqLTUyAVpFQeHZC0 | ||||
| test3:$2y$05$ih3C91zUBSTFcAh2mQnZYuob0UOZVEf16wl/ukgjDhjvj.xgM1WwS | ||||
							
								
								
									
										154
									
								
								vendor/github.com/abbot/go-http-auth/users.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								vendor/github.com/abbot/go-http-auth/users.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,154 @@ | |||
| package auth | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/csv" | ||||
| 	"os" | ||||
| 	"sync" | ||||
| ) | ||||
| 
 | ||||
| /* | ||||
|  SecretProvider is used by authenticators. Takes user name and realm | ||||
|  as an argument, returns secret required for authentication (HA1 for | ||||
|  digest authentication, properly encrypted password for basic). | ||||
| 
 | ||||
|  Returning an empty string means failing the authentication. | ||||
| */ | ||||
| type SecretProvider func(user, realm string) string | ||||
| 
 | ||||
| /* | ||||
|  Common functions for file auto-reloading | ||||
| */ | ||||
| type File struct { | ||||
| 	Path string | ||||
| 	Info os.FileInfo | ||||
| 	/* must be set in inherited types during initialization */ | ||||
| 	Reload func() | ||||
| 	mu     sync.Mutex | ||||
| } | ||||
| 
 | ||||
| func (f *File) ReloadIfNeeded() { | ||||
| 	info, err := os.Stat(f.Path) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	f.mu.Lock() | ||||
| 	defer f.mu.Unlock() | ||||
| 	if f.Info == nil || f.Info.ModTime() != info.ModTime() { | ||||
| 		f.Info = info | ||||
| 		f.Reload() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  Structure used for htdigest file authentication. Users map realms to | ||||
|  maps of users to their HA1 digests. | ||||
| */ | ||||
| type HtdigestFile struct { | ||||
| 	File | ||||
| 	Users map[string]map[string]string | ||||
| 	mu    sync.RWMutex | ||||
| } | ||||
| 
 | ||||
| func reload_htdigest(hf *HtdigestFile) { | ||||
| 	r, err := os.Open(hf.Path) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	csv_reader := csv.NewReader(r) | ||||
| 	csv_reader.Comma = ':' | ||||
| 	csv_reader.Comment = '#' | ||||
| 	csv_reader.TrimLeadingSpace = true | ||||
| 
 | ||||
| 	records, err := csv_reader.ReadAll() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 
 | ||||
| 	hf.mu.Lock() | ||||
| 	defer hf.mu.Unlock() | ||||
| 	hf.Users = make(map[string]map[string]string) | ||||
| 	for _, record := range records { | ||||
| 		_, exists := hf.Users[record[1]] | ||||
| 		if !exists { | ||||
| 			hf.Users[record[1]] = make(map[string]string) | ||||
| 		} | ||||
| 		hf.Users[record[1]][record[0]] = record[2] | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  SecretProvider implementation based on htdigest-formated files. Will | ||||
|  reload htdigest file on changes. Will panic on syntax errors in | ||||
|  htdigest files. | ||||
| */ | ||||
| func HtdigestFileProvider(filename string) SecretProvider { | ||||
| 	hf := &HtdigestFile{File: File{Path: filename}} | ||||
| 	hf.Reload = func() { reload_htdigest(hf) } | ||||
| 	return func(user, realm string) string { | ||||
| 		hf.ReloadIfNeeded() | ||||
| 		hf.mu.RLock() | ||||
| 		defer hf.mu.RUnlock() | ||||
| 		_, exists := hf.Users[realm] | ||||
| 		if !exists { | ||||
| 			return "" | ||||
| 		} | ||||
| 		digest, exists := hf.Users[realm][user] | ||||
| 		if !exists { | ||||
| 			return "" | ||||
| 		} | ||||
| 		return digest | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  Structure used for htdigest file authentication. Users map users to | ||||
|  their salted encrypted password | ||||
| */ | ||||
| type HtpasswdFile struct { | ||||
| 	File | ||||
| 	Users map[string]string | ||||
| 	mu    sync.RWMutex | ||||
| } | ||||
| 
 | ||||
| func reload_htpasswd(h *HtpasswdFile) { | ||||
| 	r, err := os.Open(h.Path) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	csv_reader := csv.NewReader(r) | ||||
| 	csv_reader.Comma = ':' | ||||
| 	csv_reader.Comment = '#' | ||||
| 	csv_reader.TrimLeadingSpace = true | ||||
| 
 | ||||
| 	records, err := csv_reader.ReadAll() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 
 | ||||
| 	h.mu.Lock() | ||||
| 	defer h.mu.Unlock() | ||||
| 	h.Users = make(map[string]string) | ||||
| 	for _, record := range records { | ||||
| 		h.Users[record[0]] = record[1] | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  SecretProvider implementation based on htpasswd-formated files. Will | ||||
|  reload htpasswd file on changes. Will panic on syntax errors in | ||||
|  htpasswd files. Realm argument of the SecretProvider is ignored. | ||||
| */ | ||||
| func HtpasswdFileProvider(filename string) SecretProvider { | ||||
| 	h := &HtpasswdFile{File: File{Path: filename}} | ||||
| 	h.Reload = func() { reload_htpasswd(h) } | ||||
| 	return func(user, realm string) string { | ||||
| 		h.ReloadIfNeeded() | ||||
| 		h.mu.RLock() | ||||
| 		password, exists := h.Users[user] | ||||
| 		h.mu.RUnlock() | ||||
| 		if !exists { | ||||
| 			return "" | ||||
| 		} | ||||
| 		return password | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										45
									
								
								vendor/github.com/abbot/go-http-auth/users_test.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								vendor/github.com/abbot/go-http-auth/users_test.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | |||
| package auth | ||||
| 
 | ||||
| import ( | ||||
| 	"os" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| func TestHtdigestFile(t *testing.T) { | ||||
| 	secrets := HtdigestFileProvider("test.htdigest") | ||||
| 	digest := secrets("test", "example.com") | ||||
| 	if digest != "aa78524fceb0e50fd8ca96dd818b8cf9" { | ||||
| 		t.Fatal("Incorrect digest for test user:", digest) | ||||
| 	} | ||||
| 	digest = secrets("test", "example1.com") | ||||
| 	if digest != "" { | ||||
| 		t.Fatal("Got digest for user in non-existant realm:", digest) | ||||
| 	} | ||||
| 	digest = secrets("test1", "example.com") | ||||
| 	if digest != "" { | ||||
| 		t.Fatal("Got digest for non-existant user:", digest) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestHtpasswdFile(t *testing.T) { | ||||
| 	secrets := HtpasswdFileProvider("test.htpasswd") | ||||
| 	passwd := secrets("test", "blah") | ||||
| 	if passwd != "{SHA}qvTGHdzF6KLavt4PO0gs2a6pQ00=" { | ||||
| 		t.Fatal("Incorrect passwd for test user:", passwd) | ||||
| 	} | ||||
| 	passwd = secrets("nosuchuser", "blah") | ||||
| 	if passwd != "" { | ||||
| 		t.Fatal("Got passwd for non-existant user:", passwd) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // TestConcurrent verifies potential race condition in users reading logic | ||||
| func TestConcurrent(t *testing.T) { | ||||
| 	secrets := HtpasswdFileProvider("test.htpasswd") | ||||
| 	os.Chtimes("test.htpasswd", time.Now(), time.Now()) | ||||
| 	go func() { | ||||
| 		secrets("test", "blah") | ||||
| 	}() | ||||
| 	secrets("test", "blah") | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue