@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 01:00 UTC

master
go 195 lines 6.29 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
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 caddyauth
16
17 import (
18 "context"
19 "net/http"
20 "net/http/httptest"
21 "sort"
22 "sync"
23 "testing"
24
25 "github.com/caddyserver/caddy/v2"
26 )
27
28 // testBasicAuthHash is a bcrypt hash (cost 4) of the plaintext "correct horse".
29 const testBasicAuthHash = "$2a$04$XziKIb5ayw0suaNwlK.1vOqOj9gEveGnkq.anOrdVrnkN5z1bA2A."
30
31 func TestBasicAuthProvisionKeepsUnknownPlaceholdersInUsername(t *testing.T) {
32 hba := HTTPBasicAuth{
33 AccountList: []Account{
34 {Username: "alice-{not-a-placeholder}", Password: testBasicAuthHash},
35 },
36 }
37 ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
38 defer cancel()
39 if err := hba.Provision(ctx); err != nil {
40 t.Fatalf("provisioning failed: %v", err)
41 }
42
43 if _, ok := hba.Accounts["alice-{not-a-placeholder}"]; !ok {
44 t.Fatalf("expected an account for the username as configured, got accounts %q", accountUsernames(hba))
45 }
46
47 req := httptest.NewRequest(http.MethodGet, "/", nil)
48 req.SetBasicAuth("alice-{not-a-placeholder}", "correct horse")
49 user, authenticated, err := hba.Authenticate(httptest.NewRecorder(), req)
50 if err != nil {
51 t.Fatalf("unexpected authentication error: %v", err)
52 }
53 if !authenticated {
54 t.Fatalf("expected authentication to succeed for the username as configured, got accounts %q", accountUsernames(hba))
55 }
56 if user.ID != "alice-{not-a-placeholder}" {
57 t.Fatalf("expected user ID %q, got %q", "alice-{not-a-placeholder}", user.ID)
58 }
59 }
60
61 func TestBasicAuthProvisionExpandsKnownPlaceholdersInUsername(t *testing.T) {
62 t.Setenv("CADDYTEST_BASICAUTH_USER", "bob")
63
64 hba := HTTPBasicAuth{
65 AccountList: []Account{
66 {Username: "{env.CADDYTEST_BASICAUTH_USER}", Password: testBasicAuthHash},
67 },
68 }
69 ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
70 defer cancel()
71 if err := hba.Provision(ctx); err != nil {
72 t.Fatalf("provisioning failed: %v", err)
73 }
74
75 req := httptest.NewRequest(http.MethodGet, "/", nil)
76 req.SetBasicAuth("bob", "correct horse")
77 _, authenticated, err := hba.Authenticate(httptest.NewRecorder(), req)
78 if err != nil {
79 t.Fatalf("unexpected authentication error: %v", err)
80 }
81 if !authenticated {
82 t.Fatalf("expected the known placeholder to expand, got accounts %q", accountUsernames(hba))
83 }
84 }
85
86 func TestBasicAuthProvisionRejectsBracedPasswordInsteadOfRewritingIt(t *testing.T) {
87 hba := HTTPBasicAuth{
88 AccountList: []Account{
89 // a password field carries a hash (MCF or base64), neither of which
90 // can contain braces, so a braced value can only be a mistake and
91 // must not be quietly rewritten into different valid base64
92 {Username: "alice", Password: "ab{cd}ef"},
93 },
94 }
95 ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
96 defer cancel()
97 err := hba.Provision(ctx)
98 if err == nil {
99 t.Fatalf("expected provisioning to fail for a password with literal braces, instead it accepted the rewritten password %q", hba.Accounts["alice"].password)
100 }
101 }
102
103 func TestBasicAuthProvisionRejectsUsernamesThatCollideAfterExpansion(t *testing.T) {
104 t.Setenv("CADDYTEST_BASICAUTH_X", "carol")
105 t.Setenv("CADDYTEST_BASICAUTH_Y", "carol")
106 t.Setenv("CADDYTEST_BASICAUTH_Z", "dave")
107
108 for _, tc := range []struct {
109 first, second string
110 wantErr bool
111 }{
112 {"carol", "carol", true},
113 {"{env.CADDYTEST_BASICAUTH_X}", "{env.CADDYTEST_BASICAUTH_Y}", true},
114 {"carol", "{env.CADDYTEST_BASICAUTH_Y}", true},
115 {"{env.CADDYTEST_BASICAUTH_X}", "{env.CADDYTEST_BASICAUTH_X}", true},
116 {"{env.CADDYTEST_BASICAUTH_X}", "{env.CADDYTEST_BASICAUTH_Z}", false},
117 } {
118 hba := HTTPBasicAuth{
119 AccountList: []Account{
120 {Username: tc.first, Password: testBasicAuthHash},
121 {Username: tc.second, Password: testBasicAuthHash},
122 },
123 }
124 ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
125 err := hba.Provision(ctx)
126 cancel()
127 if tc.wantErr && err == nil {
128 t.Errorf("%s + %s: expected a duplicate username error, got accounts %q", tc.first, tc.second, accountUsernames(hba))
129 }
130 if !tc.wantErr && err != nil {
131 t.Errorf("%s + %s: unexpected error: %v", tc.first, tc.second, err)
132 }
133 }
134 }
135
136 func accountUsernames(hba HTTPBasicAuth) []string {
137 names := make([]string, 0, len(hba.Accounts))
138 for name := range hba.Accounts {
139 names = append(names, name)
140 }
141 sort.Strings(names)
142 return names
143 }
144
145 func newCachedBasicAuth(t *testing.T) HTTPBasicAuth {
146 t.Helper()
147 hba := HTTPBasicAuth{
148 AccountList: []Account{{Username: "alice", Password: testBasicAuthHash}},
149 HashCache: new(Cache),
150 }
151 ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
152 t.Cleanup(cancel)
153 if err := hba.Provision(ctx); err != nil {
154 t.Fatalf("provisioning failed: %v", err)
155 }
156 return hba
157 }
158
159 func TestBasicAuthHashCacheDoesNotModifyStoredHash(t *testing.T) {
160 hba := newCachedBasicAuth(t)
161 account := hba.Accounts["alice"]
162
163 // inspect the full backing array, including any spare capacity
164 before := append([]byte(nil), account.password[:cap(account.password)]...)
165
166 if _, err := hba.correctPassword(account, []byte("abcd")); err != nil {
167 t.Fatalf("unexpected error: %v", err)
168 }
169
170 after := account.password[:cap(account.password)]
171 if string(before) != string(after) {
172 t.Fatalf("stored password hash buffer was modified while building the cache key")
173 }
174 }
175
176 func TestBasicAuthHashCacheConcurrentRequests(t *testing.T) {
177 hba := newCachedBasicAuth(t)
178
179 passwords := []string{"correct horse", "aaa", "bbb", "ccc"}
180 var wg sync.WaitGroup
181 for i := range 64 {
182 wg.Add(1)
183 go func() {
184 defer wg.Done()
185 pw := passwords[i%len(passwords)]
186 req := httptest.NewRequest(http.MethodGet, "/", nil)
187 req.SetBasicAuth("alice", pw)
188 _, ok, _ := hba.Authenticate(httptest.NewRecorder(), req)
189 if want := pw == "correct horse"; ok != want {
190 t.Errorf("password %q: authenticated=%v, want %v", pw, ok, want)
191 }
192 }()
193 }
194 wg.Wait()
195 }