1/*
2Copyright 2017 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package openstack
18
19import (
20	"math/rand"
21	"net/http"
22	"testing"
23	"time"
24)
25
26// testTokenGetter is a simple random token getter.
27type testTokenGetter struct{}
28
29const LetterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
30
31func RandStringBytes(n int) string {
32	b := make([]byte, n)
33	for i := range b {
34		b[i] = LetterBytes[rand.Intn(len(LetterBytes))]
35	}
36	return string(b)
37}
38
39func (*testTokenGetter) Token() (string, error) {
40	return RandStringBytes(32), nil
41}
42
43// testRoundTripper is mocked roundtripper which responds with unauthorized when
44// there is no authorization header, otherwise returns status ok.
45type testRoundTripper struct{}
46
47func (trt *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
48	authHeader := req.Header.Get("Authorization")
49	if authHeader == "" || authHeader == "Bearer " {
50		return &http.Response{
51			StatusCode: http.StatusUnauthorized,
52		}, nil
53	}
54	return &http.Response{StatusCode: http.StatusOK}, nil
55}
56
57func TestOpenstackAuthProvider(t *testing.T) {
58	trt := &tokenRoundTripper{
59		RoundTripper: &testRoundTripper{},
60	}
61
62	tests := []struct {
63		name     string
64		ttl      time.Duration
65		interval time.Duration
66		same     bool
67	}{
68		{
69			name:     "normal",
70			ttl:      2 * time.Second,
71			interval: 1 * time.Second,
72			same:     true,
73		},
74		{
75			name:     "expire",
76			ttl:      1 * time.Second,
77			interval: 2 * time.Second,
78			same:     false,
79		},
80	}
81
82	for _, test := range tests {
83		trt.tokenGetter = &cachedGetter{
84			tokenGetter: &testTokenGetter{},
85			ttl:         test.ttl,
86		}
87
88		req, err := http.NewRequest(http.MethodPost, "https://test-api-server.com", nil)
89		if err != nil {
90			t.Errorf("failed to new request: %s", err)
91		}
92		trt.RoundTrip(req)
93		header := req.Header.Get("Authorization")
94		if header == "" {
95			t.Errorf("expect to see token in header, but is absent")
96		}
97
98		time.Sleep(test.interval)
99
100		req, err = http.NewRequest(http.MethodPost, "https://test-api-server.com", nil)
101		if err != nil {
102			t.Errorf("failed to new request: %s", err)
103		}
104		trt.RoundTrip(req)
105		newHeader := req.Header.Get("Authorization")
106		if newHeader == "" {
107			t.Errorf("expect to see token in header, but is absent")
108		}
109
110		same := newHeader == header
111		if same != test.same {
112			t.Errorf("expect to get %t when compare header, but saw %t", test.same, same)
113		}
114	}
115
116}
117
118type fakePersister struct{}
119
120func (i *fakePersister) Persist(map[string]string) error {
121	return nil
122}
123
124func TestNewOpenstackAuthProvider(t *testing.T) {
125	tests := []struct {
126		name        string
127		config      map[string]string
128		expectError bool
129	}{
130		{
131			name: "normal config without openstack configurations",
132			config: map[string]string{
133				"ttl": "1s",
134				"foo": "bar",
135			},
136		},
137		{
138			name: "openstack auth provider: missing identityEndpoint",
139			config: map[string]string{
140				"ttl":        "1s",
141				"foo":        "bar",
142				"username":   "xyz",
143				"password":   "123",
144				"tenantName": "admin",
145			},
146			expectError: true,
147		},
148		{
149			name: "openstack auth provider",
150			config: map[string]string{
151				"ttl":              "1s",
152				"foo":              "bar",
153				"identityEndpoint": "http://controller:35357/v3",
154				"username":         "xyz",
155				"password":         "123",
156				"tenantName":       "admin",
157			},
158		},
159	}
160
161	for _, test := range tests {
162		_, err := newOpenstackAuthProvider("test", test.config, &fakePersister{})
163		if err != nil {
164			if !test.expectError {
165				t.Errorf("unexpected error: %v", err)
166			}
167		} else {
168			if test.expectError {
169				t.Error("expect error, but nil")
170			}
171		}
172	}
173}
174