Skip to content

Commit 123aa6f

Browse files
committed
External OAuth integration test
Signed-off-by: Simo Sorce <[email protected]>
1 parent 735a92f commit 123aa6f

File tree

1 file changed

+216
-0
lines changed

1 file changed

+216
-0
lines changed
+216
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
package integration
2+
3+
import (
4+
"bytes"
5+
"crypto/tls"
6+
"encoding/json"
7+
"fmt"
8+
"io/ioutil"
9+
"net/http"
10+
"net/http/httptest"
11+
"os"
12+
"reflect"
13+
"testing"
14+
15+
"github.com/RangelReale/osin"
16+
17+
kauthn "k8s.io/api/authentication/v1beta1"
18+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19+
"k8s.io/client-go/rest"
20+
kclientcmd "k8s.io/client-go/tools/clientcmd"
21+
kclientcmdapi "k8s.io/client-go/tools/clientcmd/api"
22+
23+
authorizationapi "github.com/openshift/origin/pkg/authorization/apis/authorization"
24+
configapi "github.com/openshift/origin/pkg/cmd/server/apis/config"
25+
oauthutil "github.com/openshift/origin/pkg/oauth/util"
26+
"github.com/openshift/origin/pkg/oc/util/tokencmd"
27+
userclient "github.com/openshift/origin/pkg/user/generated/internalclientset/typed/user/internalversion"
28+
testutil "github.com/openshift/origin/test/util"
29+
testserver "github.com/openshift/origin/test/util/server"
30+
)
31+
32+
// TestWebhookTokenAuthn checks Tokens directly against an external
33+
// authenticator
34+
func TestOauthExternal(t *testing.T) {
35+
authToken := "BoringToken"
36+
authTestUser := "user"
37+
authTestUID := "42"
38+
authTestGroups := []string{"testgroup"}
39+
40+
expectedTokenPost := kauthn.TokenReview{
41+
TypeMeta: metav1.TypeMeta{
42+
APIVersion: "authentication.k8s.io/v1beta1",
43+
Kind: "TokenReview",
44+
},
45+
Spec: kauthn.TokenReviewSpec{Token: authToken},
46+
}
47+
48+
tokenResponse := kauthn.TokenReview{
49+
TypeMeta: metav1.TypeMeta{
50+
APIVersion: "authentication.k8s.io/v1beta1",
51+
Kind: "TokenReview",
52+
},
53+
Status: kauthn.TokenReviewStatus{
54+
Authenticated: true,
55+
User: kauthn.UserInfo{
56+
Username: authTestUser,
57+
UID: authTestUID,
58+
Groups: authTestGroups,
59+
Extra: map[string]kauthn.ExtraValue{
60+
authorizationapi.ScopesKey: []string{
61+
"user:info",
62+
},
63+
},
64+
},
65+
},
66+
}
67+
68+
// Write cert we're going to use to verify auth server requests
69+
caFile, err := ioutil.TempFile("", "test.crt")
70+
if err != nil {
71+
t.Fatalf("unexpected error: %v", err)
72+
}
73+
defer os.Remove(caFile.Name())
74+
if err := ioutil.WriteFile(caFile.Name(), authLocalhostCert, os.FileMode(0600)); err != nil {
75+
t.Fatalf("unexpected error: %v", err)
76+
}
77+
78+
var authServerURL string
79+
80+
// Set up a dummy authenticator server
81+
authServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
82+
switch r.URL.Path {
83+
case "/authenticate":
84+
if r.Method != "POST" {
85+
t.Fatalf("Expected POST to /authenticate, got %s", r.Method)
86+
}
87+
if err := r.ParseForm(); err != nil {
88+
t.Fatalf("Error parsing form POSTed to /token: %v", err)
89+
}
90+
var tokenPost kauthn.TokenReview
91+
if err = json.NewDecoder(r.Body).Decode(&tokenPost); err != nil {
92+
t.Fatalf("Expected TokenReview structure in POST request: %v", err)
93+
}
94+
if !reflect.DeepEqual(tokenPost, expectedTokenPost) {
95+
t.Fatalf("Expected\n%#v\ngot\n%#v", expectedTokenPost, tokenPost)
96+
}
97+
if err = json.NewEncoder(w).Encode(tokenResponse); err != nil {
98+
t.Fatalf("Failed to encode Token Review response: %v", err)
99+
}
100+
101+
case "/oauth/authorize":
102+
w.Header().Set("Location", fmt.Sprintf("%s/oauth/token/implicit?code=%s", authServerURL, authToken))
103+
w.WriteHeader(http.StatusFound)
104+
105+
case "/oauth/token":
106+
w.Write([]byte(fmt.Sprintf(`{"access_token":%q, "token_type":"Bearer"}`, authToken)))
107+
default:
108+
t.Fatalf("Unexpected request: %v", r.URL.Path)
109+
}
110+
}))
111+
cert, err := tls.X509KeyPair(authLocalhostCert, authLocalhostKey)
112+
authServer.TLS = &tls.Config{
113+
Certificates: []tls.Certificate{cert},
114+
}
115+
authServer.StartTLS()
116+
defer authServer.Close()
117+
authServerURL = authServer.URL
118+
119+
authConfigFile, err := ioutil.TempFile("", "test.cfg")
120+
if err != nil {
121+
t.Fatalf("unexpected error: %v", err)
122+
}
123+
defer os.Remove(authConfigFile.Name())
124+
authConfigObj := kclientcmdapi.Config{
125+
Clusters: map[string]*kclientcmdapi.Cluster{
126+
"authService": {
127+
CertificateAuthority: caFile.Name(),
128+
Server: authServer.URL + "/authenticate",
129+
},
130+
},
131+
AuthInfos: map[string]*kclientcmdapi.AuthInfo{
132+
"apiServer": {
133+
ClientCertificateData: authLocalhostCert,
134+
ClientKeyData: authLocalhostKey,
135+
},
136+
},
137+
CurrentContext: "webhook",
138+
Contexts: map[string]*kclientcmdapi.Context{
139+
"webhook": {
140+
Cluster: "authService",
141+
AuthInfo: "apiServer",
142+
},
143+
},
144+
}
145+
if err := kclientcmd.WriteToFile(authConfigObj, authConfigFile.Name()); err != nil {
146+
t.Fatalf("unexpected error: %v", err)
147+
}
148+
149+
authServerMetadataFile, err := ioutil.TempFile("", "metadata.cfg")
150+
if err != nil {
151+
t.Fatalf("unexpected error: %v", err)
152+
}
153+
defer os.Remove(authServerMetadataFile.Name())
154+
authServerMetadata := oauthutil.OauthAuthorizationServerMetadata{
155+
Issuer: authServer.URL,
156+
AuthorizationEndpoint: authServer.URL + "/oauth/authorize",
157+
TokenEndpoint: authServer.URL + "/oauth/token",
158+
ResponseTypesSupported: osin.AllowedAuthorizeType{osin.CODE, osin.TOKEN},
159+
GrantTypesSupported: osin.AllowedAccessType{osin.AUTHORIZATION_CODE, "implicit"},
160+
CodeChallengeMethodsSupported: []string{"plain", "S256"},
161+
}
162+
authServerMetadataSerialized, _ := json.MarshalIndent(authServerMetadata, "", " ")
163+
authServerMetadataFile.Write(authServerMetadataSerialized)
164+
authServerMetadataFile.Sync()
165+
authServerMetadataFile.Close()
166+
167+
// Get master config
168+
masterOptions, err := testserver.DefaultMasterOptions()
169+
if err != nil {
170+
t.Fatalf("unexpected error: %v", err)
171+
}
172+
defer testserver.CleanupMasterEtcd(t, masterOptions)
173+
174+
masterOptions.OAuthConfig = nil
175+
masterOptions.AuthConfig.OAuthMetadataFile = authServerMetadataFile.Name()
176+
masterOptions.AuthConfig.WebhookTokenAuthenticators = []configapi.WebhookTokenAuthenticator{
177+
{
178+
ConfigFile: authConfigFile.Name(),
179+
CacheTTL: "10s",
180+
},
181+
}
182+
183+
// Start server
184+
clusterAdminKubeConfig, err := testserver.StartConfiguredMaster(masterOptions)
185+
if err != nil {
186+
t.Fatalf("unexpected error: %v", err)
187+
}
188+
clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig)
189+
if err != nil {
190+
t.Fatal(err)
191+
}
192+
193+
anonymousConfig := rest.AnonymousClientConfig(clusterAdminClientConfig)
194+
//The client needs to connect to both servers, so trust both certs
195+
anonymousConfig.TLSClientConfig.CAData = append(anonymousConfig.TLSClientConfig.CAData, authLocalhostCert...)
196+
197+
accessToken, err := tokencmd.RequestToken(anonymousConfig, bytes.NewBufferString("user\npass"), "", "")
198+
if err != nil {
199+
t.Errorf("Unexpected error: %v", err)
200+
}
201+
if accessToken != authToken {
202+
t.Errorf("Expected accessToken=%q, got %q", authToken, accessToken)
203+
}
204+
205+
clientConfig := rest.AnonymousClientConfig(clusterAdminClientConfig)
206+
clientConfig.BearerToken = accessToken
207+
208+
user, err := userclient.NewForConfigOrDie(clientConfig).Users().Get("~", metav1.GetOptions{})
209+
if err != nil {
210+
t.Fatal(err)
211+
}
212+
213+
if user.Name != "user" {
214+
t.Errorf("expected %v, got %v", "user", user.Name)
215+
}
216+
}

0 commit comments

Comments
 (0)