1 package challenge
2
3 import (
4 "fmt"
5 "net/http"
6 "net/url"
7 "strings"
8 "sync"
9 "testing"
10 )
11
12 func TestAuthChallengeParse(t *testing.T) {
13 header := http.Header{}
14 header.Add("WWW-Authenticate", `Bearer realm="https://auth.example.com/token",service="registry.example.com",other=fun,slashed="he\"\l\lo"`)
15
16 challenges := parseAuthHeader(header)
17 if len(challenges) != 1 {
18 t.Fatalf("Unexpected number of auth challenges: %d, expected 1", len(challenges))
19 }
20 challenge := challenges[0]
21
22 if expected := "bearer"; challenge.Scheme != expected {
23 t.Fatalf("Unexpected scheme: %s, expected: %s", challenge.Scheme, expected)
24 }
25
26 if expected := "https://auth.example.com/token"; challenge.Parameters["realm"] != expected {
27 t.Fatalf("Unexpected param: %s, expected: %s", challenge.Parameters["realm"], expected)
28 }
29
30 if expected := "registry.example.com"; challenge.Parameters["service"] != expected {
31 t.Fatalf("Unexpected param: %s, expected: %s", challenge.Parameters["service"], expected)
32 }
33
34 if expected := "fun"; challenge.Parameters["other"] != expected {
35 t.Fatalf("Unexpected param: %s, expected: %s", challenge.Parameters["other"], expected)
36 }
37
38 if expected := "he\"llo"; challenge.Parameters["slashed"] != expected {
39 t.Fatalf("Unexpected param: %s, expected: %s", challenge.Parameters["slashed"], expected)
40 }
41
42 }
43
44 func TestAuthChallengeNormalization(t *testing.T) {
45 testAuthChallengeNormalization(t, "reg.EXAMPLE.com")
46 testAuthChallengeNormalization(t, "bɿɒʜɔiɿ-ɿɘƚƨim-ƚol-ɒ-ƨʞnɒʜƚ.com")
47 testAuthChallengeNormalization(t, "reg.example.com:80")
48 testAuthChallengeConcurrent(t, "reg.EXAMPLE.com")
49 }
50
51 func testAuthChallengeNormalization(t *testing.T, host string) {
52
53 scm := NewSimpleManager()
54
55 url, err := url.Parse(fmt.Sprintf("http://%s/v2/", host))
56 if err != nil {
57 t.Fatal(err)
58 }
59
60 resp := &http.Response{
61 Request: &http.Request{
62 URL: url,
63 },
64 Header: make(http.Header),
65 StatusCode: http.StatusUnauthorized,
66 }
67 resp.Header.Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"https://%s/token\",service=\"registry.example.com\"", host))
68
69 err = scm.AddResponse(resp)
70 if err != nil {
71 t.Fatal(err)
72 }
73
74 lowered := *url
75 lowered.Host = strings.ToLower(lowered.Host)
76 lowered.Host = canonicalAddr(&lowered)
77 c, err := scm.GetChallenges(lowered)
78 if err != nil {
79 t.Fatal(err)
80 }
81
82 if len(c) == 0 {
83 t.Fatal("Expected challenge for lower-cased-host URL")
84 }
85 }
86
87 func testAuthChallengeConcurrent(t *testing.T, host string) {
88
89 scm := NewSimpleManager()
90
91 url, err := url.Parse(fmt.Sprintf("http://%s/v2/", host))
92 if err != nil {
93 t.Fatal(err)
94 }
95
96 resp := &http.Response{
97 Request: &http.Request{
98 URL: url,
99 },
100 Header: make(http.Header),
101 StatusCode: http.StatusUnauthorized,
102 }
103 resp.Header.Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"https://%s/token\",service=\"registry.example.com\"", host))
104 var s sync.WaitGroup
105 s.Add(2)
106 go func() {
107 defer s.Done()
108 for i := 0; i < 200; i++ {
109 err = scm.AddResponse(resp)
110 if err != nil {
111 t.Error(err)
112 }
113 }
114 }()
115 go func() {
116 defer s.Done()
117 lowered := *url
118 lowered.Host = strings.ToLower(lowered.Host)
119 for k := 0; k < 200; k++ {
120 _, err := scm.GetChallenges(lowered)
121 if err != nil {
122 t.Error(err)
123 }
124 }
125 }()
126 s.Wait()
127 }
128
View as plain text