func TestRegistryAndServer(t *testing.T) { ch := make(chan *http.Request, 1) assertServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { ch <- req })) validClient := &oapi.OAuthClient{ ObjectMeta: kapi.ObjectMeta{Name: "test"}, Secret: "secret", RedirectURIs: []string{assertServer.URL + "/assert"}, } validClientAuth := &oapi.OAuthClientAuthorization{ UserName: "******", ClientName: "test", } testCases := map[string]struct { Client *oapi.OAuthClient ClientAuth *oapi.OAuthClientAuthorization AuthSuccess bool AuthUser user.Info Scope string Check func(*testHandlers, *http.Request) }{ "needs auth": { Client: validClient, Check: func(h *testHandlers, _ *http.Request) { if !h.AuthNeed || h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil { t.Errorf("expected request to need authentication: %#v", h) } }, }, "needs grant": { Client: validClient, AuthSuccess: true, AuthUser: &user.DefaultInfo{ Name: "user", }, Check: func(h *testHandlers, _ *http.Request) { if h.AuthNeed || !h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil { t.Errorf("expected request to need to grant access: %#v", h) } }, }, "has non covered grant": { Client: validClient, AuthSuccess: true, AuthUser: &user.DefaultInfo{ Name: "user", }, ClientAuth: &oapi.OAuthClientAuthorization{ UserName: "******", ClientName: "test", Scopes: []string{"test"}, }, Scope: "test other", Check: func(h *testHandlers, req *http.Request) { if h.AuthNeed || !h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil { t.Errorf("expected request to need to grant access because of uncovered scopes: %#v", h) } }, }, "has covered grant": { Client: validClient, AuthSuccess: true, AuthUser: &user.DefaultInfo{ Name: "user", }, ClientAuth: &oapi.OAuthClientAuthorization{ UserName: "******", ClientName: "test", Scopes: []string{"test", "other"}, }, Scope: "test other", Check: func(h *testHandlers, req *http.Request) { if h.AuthNeed || h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil { t.Errorf("unexpected flow: %#v", h) } }, }, "has auth and grant": { Client: validClient, AuthSuccess: true, AuthUser: &user.DefaultInfo{ Name: "user", }, ClientAuth: validClientAuth, Check: func(h *testHandlers, req *http.Request) { if h.AuthNeed || h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil { t.Errorf("unexpected flow: %#v", h) return } if req == nil { t.Errorf("unexpected nil assertion request") return } if code := req.URL.Query().Get("code"); code == "" { t.Errorf("expected query param 'code', got: %#v", req) } }, }, } for _, testCase := range testCases { h := &testHandlers{} h.Authenticate = testCase.AuthSuccess h.User = testCase.AuthUser access, authorize := &test.AccessTokenRegistry{}, &test.AuthorizeTokenRegistry{} client := &test.ClientRegistry{ Client: testCase.Client, } if testCase.Client == nil { client.Err = apierrs.NewNotFound("client", "unknown") } grant := &test.ClientAuthorizationRegistry{ ClientAuthorization: testCase.ClientAuth, } if testCase.ClientAuth == nil { grant.Err = apierrs.NewNotFound("clientAuthorization", "test:test") } storage := registrystorage.New(access, authorize, client, NewUserConversion()) config := osinserver.NewDefaultServerConfig() server := osinserver.New( config, storage, osinserver.AuthorizeHandlers{ handlers.NewAuthorizeAuthenticator( h, h, h, ), handlers.NewGrantCheck( NewClientAuthorizationGrantChecker(grant), h, h, ), }, osinserver.AccessHandlers{ handlers.NewDenyAccessAuthenticator(), }, h, ) mux := http.NewServeMux() server.Install(mux, "") s := httptest.NewServer(mux) oaclientConfig := &osincli.ClientConfig{ ClientId: "test", ClientSecret: "secret", RedirectUrl: assertServer.URL + "/assert", AuthorizeUrl: s.URL + "/authorize", TokenUrl: s.URL + "/token", Scope: testCase.Scope, } oaclient, err := osincli.NewClient(oaclientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } aReq := oaclient.NewAuthorizeRequest(osincli.CODE) if _, err := http.Get(aReq.GetAuthorizeUrl().String()); err != nil { t.Fatalf("unexpected error: %v", err) } var req *http.Request select { case out := <-ch: req = out default: } testCase.Check(h, req) } }
func TestCLIGetToken(t *testing.T) { testutil.DeleteAllEtcdKeys() // setup etcdClient := testutil.NewEtcdClient() etcdHelper, _ := master.NewEtcdHelper(etcdClient, latest.Version, etcdtest.PathPrefix()) accessTokenStorage := accesstokenetcd.NewREST(etcdHelper) accessTokenRegistry := accesstokenregistry.NewRegistry(accessTokenStorage) authorizeTokenStorage := authorizetokenetcd.NewREST(etcdHelper) authorizeTokenRegistry := authorizetokenregistry.NewRegistry(authorizeTokenStorage) clientStorage := clientetcd.NewREST(etcdHelper) clientRegistry := clientregistry.NewRegistry(clientStorage) clientAuthStorage := clientauthetcd.NewREST(etcdHelper) clientAuthRegistry := clientauthregistry.NewRegistry(clientAuthStorage) userStorage := useretcd.NewREST(etcdHelper) userRegistry := userregistry.NewRegistry(userStorage) identityStorage := identityetcd.NewREST(etcdHelper) identityRegistry := identityregistry.NewRegistry(identityStorage) identityMapper := identitymapper.NewAlwaysCreateUserIdentityToUserMapper(identityRegistry, userRegistry) authRequestHandler := basicauthrequest.NewBasicAuthAuthentication(allowanypassword.New("get-token-test", identityMapper), true) authHandler := oauthhandlers.NewUnionAuthenticationHandler( map[string]oauthhandlers.AuthenticationChallenger{"login": passwordchallenger.NewBasicAuthChallenger("openshift")}, nil, nil) storage := registrystorage.New(accessTokenRegistry, authorizeTokenRegistry, clientRegistry, oauthregistry.NewUserConversion()) config := osinserver.NewDefaultServerConfig() grantChecker := oauthregistry.NewClientAuthorizationGrantChecker(clientAuthRegistry) grantHandler := oauthhandlers.NewAutoGrant() server := osinserver.New( config, storage, osinserver.AuthorizeHandlers{ oauthhandlers.NewAuthorizeAuthenticator( authRequestHandler, authHandler, oauthhandlers.EmptyError{}, ), oauthhandlers.NewGrantCheck( grantChecker, grantHandler, oauthhandlers.EmptyError{}, ), }, osinserver.AccessHandlers{ oauthhandlers.NewDenyAccessAuthenticator(), }, osinserver.NewDefaultErrorHandler(), ) mux := http.NewServeMux() server.Install(mux, origin.OpenShiftOAuthAPIPrefix) oauthServer := httptest.NewServer(http.Handler(mux)) defer oauthServer.Close() t.Logf("oauth server is on %v\n", oauthServer.URL) // create the default oauth clients with redirects to our server origin.CreateOrUpdateDefaultOAuthClients(oauthServer.URL, []string{oauthServer.URL}, clientRegistry) flags := pflag.NewFlagSet("test-flags", pflag.ContinueOnError) clientCfg := clientcmd.NewConfig() clientCfg.Bind(flags) flags.Parse(strings.Split("--master="+oauthServer.URL, " ")) reader := bytes.NewBufferString("user\npass") accessToken, err := tokencmd.RequestToken(clientCfg.OpenShiftConfig(), reader, "", "") if err != nil { t.Errorf("Unexpected error: %v", err) } if len(accessToken) == 0 { t.Error("Expected accessToken, but did not get one") } // lets see if this access token is any good token, err := accessTokenRegistry.GetAccessToken(kapi.NewContext(), accessToken) if err != nil { t.Errorf("Unexpected error: %v", err) } if token.UserName != "user" { t.Errorf("Expected token for \"user\", but got: %#v", token) } }
func TestOAuthStorage(t *testing.T) { testutil.DeleteAllEtcdKeys() interfaces, _ := latest.InterfacesFor(latest.Version) etcdClient := testutil.NewEtcdClient() etcdHelper := tools.NewEtcdHelper(etcdClient, interfaces.Codec, etcdtest.PathPrefix()) accessTokenStorage := accesstokenetcd.NewREST(etcdHelper) accessTokenRegistry := accesstokenregistry.NewRegistry(accessTokenStorage) authorizeTokenStorage := authorizetokenetcd.NewREST(etcdHelper) authorizeTokenRegistry := authorizetokenregistry.NewRegistry(authorizeTokenStorage) clientStorage := clientetcd.NewREST(etcdHelper) clientRegistry := clientregistry.NewRegistry(clientStorage) user := &testUser{UserName: "******", UserUID: "1"} storage := registrystorage.New(accessTokenRegistry, authorizeTokenRegistry, clientRegistry, user) oauthServer := osinserver.New( osinserver.NewDefaultServerConfig(), storage, osinserver.AuthorizeHandlerFunc(func(ar *osin.AuthorizeRequest, w http.ResponseWriter) (bool, error) { ar.UserData = "test" ar.Authorized = true return false, nil }), osinserver.AccessHandlerFunc(func(ar *osin.AccessRequest, w http.ResponseWriter) error { ar.UserData = "test" ar.Authorized = true ar.GenerateRefresh = false return nil }), osinserver.NewDefaultErrorHandler(), ) mux := http.NewServeMux() oauthServer.Install(mux, "") server := httptest.NewServer(mux) defer server.Close() ch := make(chan *osincli.AccessData, 1) var oaclient *osincli.Client var authReq *osincli.AuthorizeRequest assertServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { data, err := authReq.HandleRequest(r) if err != nil { t.Fatalf("unexpected error: %v", err) } tokenReq := oaclient.NewAccessRequest(osincli.AUTHORIZATION_CODE, data) token, err := tokenReq.GetToken() if err != nil { t.Fatalf("unexpected error: %v", err) } ch <- token })) clientRegistry.CreateClient(kapi.NewContext(), &api.OAuthClient{ ObjectMeta: kapi.ObjectMeta{Name: "test"}, Secret: "secret", RedirectURIs: []string{assertServer.URL + "/assert"}, }) storedClient, err := storage.GetClient("test") if err != nil { t.Fatalf("unexpected error: %v", err) } if storedClient.GetSecret() != "secret" { t.Fatalf("unexpected stored client: %#v", storedClient) } oaclientConfig := &osincli.ClientConfig{ ClientId: "test", ClientSecret: "secret", RedirectUrl: assertServer.URL + "/assert", AuthorizeUrl: server.URL + "/authorize", TokenUrl: server.URL + "/token", } osinclient, err := osincli.NewClient(oaclientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } oaclient = osinclient // initialize the assert server client as well authReq = oaclient.NewAuthorizeRequest(osincli.CODE) config := &oauth2.Config{ ClientID: "test", ClientSecret: "", Scopes: []string{"a_scope"}, RedirectURL: assertServer.URL + "/assert", Endpoint: oauth2.Endpoint{ AuthURL: server.URL + "/authorize", TokenURL: server.URL + "/token", }, } url := config.AuthCodeURL("") client := http.Client{ /*CheckRedirect: func(req *http.Request, via []*http.Request) error { t.Logf("redirect (%d): to %s, %#v", len(via), req.URL, req) return nil }*/} resp, err := client.Get(url) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.StatusCode != http.StatusOK { t.Fatalf("unexpected response: %#v", resp) } token := <-ch if token.AccessToken == "" { t.Errorf("unexpected access token: %#v", token) } actualToken, err := accessTokenRegistry.GetAccessToken(kapi.NewContext(), token.AccessToken) if err != nil { t.Fatalf("unexpected error: %v", err) } if actualToken.UserUID != "1" || actualToken.UserName != "test" { t.Errorf("unexpected stored token: %#v", actualToken) } }
func TestAuthProxyOnAuthorize(t *testing.T) { testutil.DeleteAllEtcdKeys() // setup etcdClient := testutil.NewEtcdClient() etcdHelper, _ := master.NewEtcdHelper(etcdClient, latest.Version, etcdtest.PathPrefix()) accessTokenStorage := accesstokenetcd.NewREST(etcdHelper) accessTokenRegistry := accesstokenregistry.NewRegistry(accessTokenStorage) authorizeTokenStorage := authorizetokenetcd.NewREST(etcdHelper) authorizeTokenRegistry := authorizetokenregistry.NewRegistry(authorizeTokenStorage) clientStorage := clientetcd.NewREST(etcdHelper) clientRegistry := clientregistry.NewRegistry(clientStorage) clientAuthStorage := clientauthetcd.NewREST(etcdHelper) clientAuthRegistry := clientauthregistry.NewRegistry(clientAuthStorage) userStorage := useretcd.NewREST(etcdHelper) userRegistry := userregistry.NewRegistry(userStorage) identityStorage := identityetcd.NewREST(etcdHelper) identityRegistry := identityregistry.NewRegistry(identityStorage) identityMapper := identitymapper.NewAlwaysCreateUserIdentityToUserMapper(identityRegistry, userRegistry) // this auth request handler is the one that is supposed to recognize information from a front proxy authRequestHandler := headerrequest.NewAuthenticator("front-proxy-test", headerrequest.NewDefaultConfig(), identityMapper) authHandler := &oauthhandlers.EmptyAuth{} storage := registrystorage.New(accessTokenRegistry, authorizeTokenRegistry, clientRegistry, oauthregistry.NewUserConversion()) config := osinserver.NewDefaultServerConfig() grantChecker := oauthregistry.NewClientAuthorizationGrantChecker(clientAuthRegistry) grantHandler := oauthhandlers.NewAutoGrant() server := osinserver.New( config, storage, osinserver.AuthorizeHandlers{ oauthhandlers.NewAuthorizeAuthenticator( authRequestHandler, authHandler, oauthhandlers.EmptyError{}, ), oauthhandlers.NewGrantCheck( grantChecker, grantHandler, oauthhandlers.EmptyError{}, ), }, osinserver.AccessHandlers{ oauthhandlers.NewDenyAccessAuthenticator(), }, osinserver.NewDefaultErrorHandler(), ) mux := http.NewServeMux() server.Install(mux, origin.OpenShiftOAuthAPIPrefix) oauthServer := httptest.NewServer(http.Handler(mux)) defer oauthServer.Close() t.Logf("oauth server is on %v\n", oauthServer.URL) // set up a front proxy guarding the oauth server proxyHTTPHandler := NewBasicAuthChallenger("TestRegistryAndServer", validUsers, NewXRemoteUserProxyingHandler(oauthServer.URL)) proxyServer := httptest.NewServer(proxyHTTPHandler) defer proxyServer.Close() t.Logf("proxy server is on %v\n", proxyServer.URL) // need to prime clients so that we can get back a code. the client must be valid createClient(t, clientRegistry, &oauthapi.OAuthClient{ObjectMeta: kapi.ObjectMeta{Name: "test"}, Secret: "secret", RedirectURIs: []string{oauthServer.URL}}) // our simple URL to get back a code. We want to go through the front proxy rawAuthorizeRequest := proxyServer.URL + origin.OpenShiftOAuthAPIPrefix + "/authorize?response_type=code&client_id=test" // the first request we make to the front proxy should challenge us for authentication info shouldBeAChallengeResponse, err := http.Get(rawAuthorizeRequest) if err != nil { t.Errorf("Unexpected error: %v", err) } if shouldBeAChallengeResponse.StatusCode != http.StatusUnauthorized { t.Errorf("Expected Unauthorized, but got %v", shouldBeAChallengeResponse.StatusCode) } // create an http.Client to make our next request. We need a custom Transport to authenticate us through our front proxy // and a custom CheckRedirect so that we can keep track of the redirect responses we're getting // OAuth requests a few redirects that we don't really care about checking, so this simpler than using a round tripper // and manually handling redirects and setting our auth information every time for the front proxy redirectedUrls := make([]url.URL, 10) httpClient := http.Client{ CheckRedirect: getRedirectMethod(t, &redirectedUrls), Transport: kclient.NewBasicAuthRoundTripper("sanefarmer", "who?", http.DefaultTransport), } // make our authorize request again, but this time our transport has properly set the auth info for the front proxy req, err := http.NewRequest("GET", rawAuthorizeRequest, nil) _, err = httpClient.Do(req) if err != nil { t.Errorf("Unexpected error: %v", err) } // check the last redirect and see if we got a code foundCode := "" if len(redirectedUrls) > 0 { foundCode = redirectedUrls[len(redirectedUrls)-1].Query().Get("code") } if len(foundCode) == 0 { t.Errorf("Did not find code in any redirect: %v", redirectedUrls) } else { t.Logf("Found code %v\n", foundCode) } }
// InstallAPI registers endpoints for an OAuth2 server into the provided mux, // then returns an array of strings indicating what endpoints were started // (these are format strings that will expect to be sent a single string value). func (c *AuthConfig) InstallAPI(container *restful.Container) []string { // TODO: register into container mux := container.ServeMux accessTokenStorage := accesstokenetcd.NewREST(c.EtcdHelper) accessTokenRegistry := accesstokenregistry.NewRegistry(accessTokenStorage) authorizeTokenStorage := authorizetokenetcd.NewREST(c.EtcdHelper) authorizeTokenRegistry := authorizetokenregistry.NewRegistry(authorizeTokenStorage) clientStorage := clientetcd.NewREST(c.EtcdHelper) clientRegistry := clientregistry.NewRegistry(clientStorage) clientAuthStorage := clientauthetcd.NewREST(c.EtcdHelper) clientAuthRegistry := clientauthregistry.NewRegistry(clientAuthStorage) authRequestHandler, authHandler, authFinalizer, err := c.getAuthorizeAuthenticationHandlers(mux) if err != nil { glog.Fatal(err) } storage := registrystorage.New(accessTokenRegistry, authorizeTokenRegistry, clientRegistry, registry.NewUserConversion()) config := osinserver.NewDefaultServerConfig() if c.Options.TokenConfig.AuthorizeTokenMaxAgeSeconds > 0 { config.AuthorizationExpiration = c.Options.TokenConfig.AuthorizeTokenMaxAgeSeconds } if c.Options.TokenConfig.AccessTokenMaxAgeSeconds > 0 { config.AccessExpiration = c.Options.TokenConfig.AccessTokenMaxAgeSeconds } grantChecker := registry.NewClientAuthorizationGrantChecker(clientAuthRegistry) grantHandler := c.getGrantHandler(mux, authRequestHandler, clientRegistry, clientAuthRegistry) server := osinserver.New( config, storage, osinserver.AuthorizeHandlers{ handlers.NewAuthorizeAuthenticator( authRequestHandler, authHandler, handlers.EmptyError{}, ), handlers.NewGrantCheck( grantChecker, grantHandler, handlers.EmptyError{}, ), authFinalizer, }, osinserver.AccessHandlers{ handlers.NewDenyAccessAuthenticator(), }, osinserver.NewDefaultErrorHandler(), ) server.Install(mux, OpenShiftOAuthAPIPrefix) CreateOrUpdateDefaultOAuthClients(c.Options.MasterPublicURL, c.AssetPublicAddresses, clientRegistry) osOAuthClientConfig := c.NewOpenShiftOAuthClientConfig(&OSBrowserClientBase) osOAuthClientConfig.RedirectUrl = c.Options.MasterPublicURL + path.Join(OpenShiftOAuthAPIPrefix, tokenrequest.DisplayTokenEndpoint) osOAuthClient, _ := osincli.NewClient(osOAuthClientConfig) if c.MasterRoots != nil { // Copy the default transport var transport http.Transport = *http.DefaultTransport.(*http.Transport) // Set TLS CA roots transport.TLSClientConfig = &tls.Config{RootCAs: c.MasterRoots} osOAuthClient.Transport = &transport } tokenRequestEndpoints := tokenrequest.NewEndpoints(c.Options.MasterPublicURL, osOAuthClient) tokenRequestEndpoints.Install(mux, OpenShiftOAuthAPIPrefix) // glog.Infof("oauth server configured as: %#v", server) // glog.Infof("auth handler: %#v", authHandler) // glog.Infof("auth request handler: %#v", authRequestHandler) // glog.Infof("grant checker: %#v", grantChecker) // glog.Infof("grant handler: %#v", grantHandler) return []string{ fmt.Sprintf("Started OAuth2 API at %%s%s", OpenShiftOAuthAPIPrefix), fmt.Sprintf("Started Login endpoint at %%s%s", OpenShiftLoginPrefix), } }