func (c *AuthConfig) getPasswordAuthenticator(identityProvider configapi.IdentityProvider) (authenticator.Password, error) { identityMapper := identitymapper.NewAlwaysCreateUserIdentityToUserMapper(c.IdentityRegistry, c.UserRegistry) switch provider := identityProvider.Provider.Object.(type) { case (*configapi.AllowAllPasswordIdentityProvider): return allowanypassword.New(identityProvider.Name, identityMapper), nil case (*configapi.DenyAllPasswordIdentityProvider): return denypassword.New(), nil case (*configapi.LDAPPasswordIdentityProvider): url, err := ldaputil.ParseURL(provider.URL) if err != nil { return nil, fmt.Errorf("Error parsing LDAPPasswordIdentityProvider URL: %v", err) } clientConfig, err := ldaputil.NewLDAPClientConfig(provider.URL, provider.BindDN, provider.BindPassword, provider.CA, provider.Insecure) if err != nil { return nil, err } opts := ldappassword.Options{ URL: url, ClientConfig: clientConfig, UserAttributeDefiner: ldaputil.NewLDAPUserAttributeDefiner(provider.Attributes), } return ldappassword.New(identityProvider.Name, opts, identityMapper) case (*configapi.HTPasswdPasswordIdentityProvider): htpasswdFile := provider.File if len(htpasswdFile) == 0 { return nil, fmt.Errorf("HTPasswdFile is required to support htpasswd auth") } if htpasswordAuth, err := htpasswd.New(identityProvider.Name, htpasswdFile, identityMapper); err != nil { return nil, fmt.Errorf("Error loading htpasswd file %s: %v", htpasswdFile, err) } else { return htpasswordAuth, nil } case (*configapi.BasicAuthPasswordIdentityProvider): connectionInfo := provider.RemoteConnectionInfo if len(connectionInfo.URL) == 0 { return nil, fmt.Errorf("URL is required for BasicAuthPasswordIdentityProvider") } transport, err := cmdutil.TransportFor(connectionInfo.CA, connectionInfo.ClientCert.CertFile, connectionInfo.ClientCert.KeyFile) if err != nil { return nil, fmt.Errorf("Error building BasicAuthPasswordIdentityProvider client: %v", err) } return basicauthpassword.New(identityProvider.Name, connectionInfo.URL, transport, identityMapper), nil default: return nil, fmt.Errorf("No password auth found that matches %v. The OAuth server cannot start!", identityProvider) } }
func (c *AuthConfig) getAuthenticationRequestHandler() (authenticator.Request, error) { var authRequestHandlers []authenticator.Request if c.SessionAuth != nil { authRequestHandlers = append(authRequestHandlers, c.SessionAuth) } for _, identityProvider := range c.Options.IdentityProviders { identityMapper := identitymapper.NewAlwaysCreateUserIdentityToUserMapper(c.IdentityRegistry, c.UserRegistry) if configapi.IsPasswordAuthenticator(identityProvider) { passwordAuthenticator, err := c.getPasswordAuthenticator(identityProvider) if err != nil { return nil, err } authRequestHandlers = append(authRequestHandlers, basicauthrequest.NewBasicAuthAuthentication(passwordAuthenticator, true)) } else { switch provider := identityProvider.Provider.Object.(type) { case (*configapi.RequestHeaderIdentityProvider): var authRequestHandler authenticator.Request authRequestConfig := &headerrequest.Config{ UserNameHeaders: provider.Headers, } authRequestHandler = headerrequest.NewAuthenticator(identityProvider.Name, authRequestConfig, identityMapper) // Wrap with an x509 verifier if len(provider.ClientCA) > 0 { caData, err := ioutil.ReadFile(provider.ClientCA) if err != nil { return nil, fmt.Errorf("Error reading %s: %v", provider.ClientCA, err) } opts := x509request.DefaultVerifyOptions() opts.Roots = x509.NewCertPool() if ok := opts.Roots.AppendCertsFromPEM(caData); !ok { return nil, fmt.Errorf("Error loading certs from %s: %v", provider.ClientCA, err) } authRequestHandler = x509request.NewVerifier(opts, authRequestHandler) } authRequestHandlers = append(authRequestHandlers, authRequestHandler) } } } authRequestHandler := unionrequest.NewUnionAuthentication(authRequestHandlers...) return authRequestHandler, nil }
func TestUserInitialization(t *testing.T) { masterConfig, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } etcdClient, err := etcd.GetAndTestEtcdClient(masterConfig.EtcdClientInfo) if err != nil { t.Errorf("unexpected error: %v", err) } etcdHelper, err := origin.NewEtcdStorage(etcdClient, masterConfig.EtcdStorageConfig.OpenShiftStorageVersion, masterConfig.EtcdStorageConfig.OpenShiftStoragePrefix) if err != nil { t.Errorf("unexpected error: %v", err) } userRegistry := userregistry.NewRegistry(useretcd.NewREST(etcdHelper)) identityRegistry := identityregistry.NewRegistry(identityetcd.NewREST(etcdHelper)) useridentityMappingRegistry := useridentitymapping.NewRegistry(useridentitymapping.NewREST(userRegistry, identityRegistry)) lookup := identitymapper.NewLookupIdentityMapper(useridentityMappingRegistry, userRegistry) provisioner := identitymapper.NewAlwaysCreateUserIdentityToUserMapper(identityRegistry, userRegistry) testcases := map[string]struct { Identity authapi.UserIdentityInfo Mapper authapi.UserIdentityMapper CreateIdentity *api.Identity CreateUser *api.User CreateMapping *api.UserIdentityMapping UpdateUser *api.User ExpectedErr error ExpectedUserName string ExpectedFullName string }{ "lookup missing identity": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: lookup, ExpectedErr: kerrs.NewNotFound("UserIdentityMapping", "idp:bob"), }, "lookup existing identity": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: lookup, CreateUser: makeUser("mappeduser"), CreateIdentity: makeIdentity("idp", "bob"), CreateMapping: makeMapping("mappeduser", "idp:bob"), ExpectedUserName: "******", }, "provision missing identity and user": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: provisioner, ExpectedUserName: "******", }, "provision missing identity and user with preferred username and display name": { Identity: makeIdentityInfo("idp", "bob", map[string]string{authapi.IdentityDisplayNameKey: "Bob, Sr.", authapi.IdentityPreferredUsernameKey: "admin"}), Mapper: provisioner, ExpectedUserName: "******", ExpectedFullName: "Bob, Sr.", }, "provision missing identity for existing user": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: provisioner, CreateUser: makeUser("bob", "idp:bob"), ExpectedUserName: "******", }, "provision missing identity with conflicting user": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: provisioner, CreateUser: makeUser("bob"), ExpectedUserName: "******", }, "provision missing identity with conflicting user and preferred username": { Identity: makeIdentityInfo("idp", "bob", map[string]string{authapi.IdentityPreferredUsernameKey: "admin"}), Mapper: provisioner, CreateUser: makeUser("admin"), ExpectedUserName: "******", }, "provision with existing unmapped identity": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: provisioner, CreateIdentity: makeIdentity("idp", "bob"), ExpectedErr: kerrs.NewNotFound("UserIdentityMapping", "idp:bob"), }, "provision with existing mapped identity with invalid user UID": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: provisioner, CreateUser: makeUser("mappeduser"), CreateIdentity: makeIdentityWithUserReference("idp", "bob", "mappeduser", "invalidUID"), ExpectedErr: kerrs.NewNotFound("UserIdentityMapping", "idp:bob"), }, "provision with existing mapped identity without user backreference": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: provisioner, CreateUser: makeUser("mappeduser"), CreateIdentity: makeIdentity("idp", "bob"), CreateMapping: makeMapping("mappeduser", "idp:bob"), // Update user to a version which does not reference the identity UpdateUser: makeUser("mappeduser"), ExpectedErr: kerrs.NewNotFound("UserIdentityMapping", "idp:bob"), }, "provision returns existing mapping": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: provisioner, CreateUser: makeUser("mappeduser"), CreateIdentity: makeIdentity("idp", "bob"), CreateMapping: makeMapping("mappeduser", "idp:bob"), ExpectedUserName: "******", }, } for k, testcase := range testcases { // Cleanup if err := etcdHelper.RecursiveDelete(useretcd.EtcdPrefix, true); err != nil && !etcdstorage.IsEtcdNotFound(err) { t.Fatalf("Could not clean up users: %v", err) } if err := etcdHelper.RecursiveDelete(identityetcd.EtcdPrefix, true); err != nil && !etcdstorage.IsEtcdNotFound(err) { t.Fatalf("Could not clean up identities: %v", err) } // Pre-create items if testcase.CreateUser != nil { _, err := clusterAdminClient.Users().Create(testcase.CreateUser) if err != nil { t.Errorf("%s: Could not create user: %v", k, err) continue } } if testcase.CreateIdentity != nil { _, err := clusterAdminClient.Identities().Create(testcase.CreateIdentity) if err != nil { t.Errorf("%s: Could not create identity: %v", k, err) continue } } if testcase.CreateMapping != nil { _, err := clusterAdminClient.UserIdentityMappings().Update(testcase.CreateMapping) if err != nil { t.Errorf("%s: Could not create mapping: %v", k, err) continue } } if testcase.UpdateUser != nil { if testcase.UpdateUser.ResourceVersion == "" { existingUser, err := clusterAdminClient.Users().Get(testcase.UpdateUser.Name) if err != nil { t.Errorf("%s: Could not get user to update: %v", k, err) continue } testcase.UpdateUser.ResourceVersion = existingUser.ResourceVersion } _, err := clusterAdminClient.Users().Update(testcase.UpdateUser) if err != nil { t.Errorf("%s: Could not update user: %v", k, err) continue } } // Spawn 5 simultaneous mappers to test race conditions var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go func() { defer wg.Done() userInfo, err := testcase.Mapper.UserFor(testcase.Identity) if err != nil { if testcase.ExpectedErr == nil { t.Errorf("%s: Expected success, got error '%v'", k, err) } else if err.Error() != testcase.ExpectedErr.Error() { t.Errorf("%s: Expected error %v, got '%v'", k, testcase.ExpectedErr.Error(), err) } return } if err == nil && testcase.ExpectedErr != nil { t.Errorf("%s: Expected error '%v', got none", k, testcase.ExpectedErr) return } if userInfo.GetName() != testcase.ExpectedUserName { t.Errorf("%s: Expected username %s, got %s", k, testcase.ExpectedUserName, userInfo.GetName()) return } user, err := clusterAdminClient.Users().Get(userInfo.GetName()) if err != nil { t.Errorf("%s: Error getting user: %v", k, err) } if user.FullName != testcase.ExpectedFullName { t.Errorf("%s: Expected full name %s, got %s", k, testcase.ExpectedFullName, user.FullName) } }() } wg.Wait() } }
func TestCLIGetToken(t *testing.T) { testutil.DeleteAllEtcdKeys() // setup etcdClient := testutil.NewEtcdClient() etcdHelper, _ := master.NewEtcdStorage(etcdClient, latest.InterfacesFor, 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 (c *AuthConfig) getAuthenticationHandler(mux cmdutil.Mux, errorHandler handlers.AuthenticationErrorHandler) (handlers.AuthenticationHandler, error) { challengers := map[string]handlers.AuthenticationChallenger{} redirectors := map[string]handlers.AuthenticationRedirector{} for _, identityProvider := range c.Options.IdentityProviders { identityMapper := identitymapper.NewAlwaysCreateUserIdentityToUserMapper(c.IdentityRegistry, c.UserRegistry) // TODO: refactor handler building per type if configapi.IsPasswordAuthenticator(identityProvider) { passwordAuth, err := c.getPasswordAuthenticator(identityProvider) if err != nil { return nil, err } if identityProvider.UseAsLogin { // Password auth requires: // 1. a session success handler (to remember you logged in) // 2. a redirectSuccessHandler (to go back to the "then" param) if c.SessionAuth == nil { return nil, errors.New("SessionAuth is required for password-based login") } passwordSuccessHandler := handlers.AuthenticationSuccessHandlers{c.SessionAuth, redirectSuccessHandler{}} // Since we're redirecting to a local login page, we don't need to force absolute URL resolution redirectors["login-"+identityProvider.Name+"-redirect"] = redirector.NewRedirector(nil, OpenShiftLoginPrefix+"?then=${url}") var loginTemplateFile string if c.Options.Templates != nil { loginTemplateFile = c.Options.Templates.Login } loginFormRenderer, err := login.NewLoginFormRenderer(loginTemplateFile) if err != nil { return nil, err } login := login.NewLogin(c.getCSRF(), &callbackPasswordAuthenticator{passwordAuth, passwordSuccessHandler}, loginFormRenderer) login.Install(mux, OpenShiftLoginPrefix) } if identityProvider.UseAsChallenger { // For now, all password challenges share a single basic challenger, since they'll all respond to any basic credentials challengers["basic-challenge"] = passwordchallenger.NewBasicAuthChallenger("openshift") } } else if configapi.IsOAuthIdentityProvider(identityProvider) { oauthProvider, err := c.getOAuthProvider(identityProvider) if err != nil { return nil, err } // Default state builder, combining CSRF and return URL handling state := external.CSRFRedirectingState(c.getCSRF()) // OAuth auth requires // 1. a session success handler (to remember you logged in) // 2. a state success handler (to go back to the URL encoded in the state) if c.SessionAuth == nil { return nil, errors.New("SessionAuth is required for OAuth-based login") } oauthSuccessHandler := handlers.AuthenticationSuccessHandlers{c.SessionAuth, state} // If the specified errorHandler doesn't handle the login error, let the state error handler attempt to propagate specific errors back to the token requester oauthErrorHandler := handlers.AuthenticationErrorHandlers{errorHandler, state} callbackPath := path.Join(OpenShiftOAuthCallbackPrefix, identityProvider.Name) oauthHandler, err := external.NewExternalOAuthRedirector(oauthProvider, state, c.Options.MasterPublicURL+callbackPath, oauthSuccessHandler, oauthErrorHandler, identityMapper) if err != nil { return nil, fmt.Errorf("unexpected error: %v", err) } mux.Handle(callbackPath, oauthHandler) if identityProvider.UseAsLogin { redirectors["oauth-"+identityProvider.Name+"-redirect"] = oauthHandler } if identityProvider.UseAsChallenger { return nil, errors.New("oauth identity providers cannot issue challenges") } } else if requestHeaderProvider, isRequestHeader := identityProvider.Provider.Object.(*configapi.RequestHeaderIdentityProvider); isRequestHeader { // We might be redirecting to an external site, we need to fully resolve the request URL to the public master baseRequestURL, err := url.Parse(c.Options.MasterPublicURL + OpenShiftOAuthAPIPrefix + osinserver.AuthorizePath) if err != nil { return nil, err } if identityProvider.UseAsChallenger { challengers["requestheader-"+identityProvider.Name+"-redirect"] = redirector.NewChallenger(baseRequestURL, requestHeaderProvider.ChallengeURL) } if identityProvider.UseAsLogin { redirectors["requestheader-"+identityProvider.Name+"-redirect"] = redirector.NewRedirector(baseRequestURL, requestHeaderProvider.LoginURL) } } } if len(redirectors) > 0 && len(challengers) == 0 { // Add a default challenger that will warn and give a link to the web browser token-granting location challengers["placeholder"] = placeholderchallenger.New(OpenShiftOAuthTokenRequestURL(c.Options.MasterPublicURL)) } authHandler := handlers.NewUnionAuthenticationHandler(challengers, redirectors, errorHandler) return authHandler, nil }
func TestAuthProxyOnAuthorize(t *testing.T) { testutil.DeleteAllEtcdKeys() // setup etcdClient := testutil.NewEtcdClient() etcdHelper, _ := master.NewEtcdStorage(etcdClient, latest.InterfacesFor, 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) } }
func (c *AuthConfig) getPasswordAuthenticator(identityProvider configapi.IdentityProvider) (authenticator.Password, error) { identityMapper := identitymapper.NewAlwaysCreateUserIdentityToUserMapper(c.IdentityRegistry, c.UserRegistry) switch provider := identityProvider.Provider.Object.(type) { case (*configapi.AllowAllPasswordIdentityProvider): return allowanypassword.New(identityProvider.Name, identityMapper), nil case (*configapi.DenyAllPasswordIdentityProvider): return denypassword.New(), nil case (*configapi.LDAPPasswordIdentityProvider): url, err := ldappassword.ParseURL(provider.URL) if err != nil { return nil, fmt.Errorf("Error parsing LDAPPasswordIdentityProvider URL: %v", err) } tlsConfig := &tls.Config{} if len(provider.CA) > 0 { roots, err := util.CertPoolFromFile(provider.CA) if err != nil { return nil, fmt.Errorf("error loading cert pool from ca file %s: %v", provider.CA, err) } tlsConfig.RootCAs = roots } opts := ldappassword.Options{ URL: url, Insecure: provider.Insecure, TLSConfig: tlsConfig, BindDN: provider.BindDN, BindPassword: provider.BindPassword, AttributeEmail: provider.Attributes.Email, AttributeName: provider.Attributes.Name, AttributeID: provider.Attributes.ID, AttributePreferredUsername: provider.Attributes.PreferredUsername, } return ldappassword.New(identityProvider.Name, opts, identityMapper) case (*configapi.HTPasswdPasswordIdentityProvider): htpasswdFile := provider.File if len(htpasswdFile) == 0 { return nil, fmt.Errorf("HTPasswdFile is required to support htpasswd auth") } if htpasswordAuth, err := htpasswd.New(identityProvider.Name, htpasswdFile, identityMapper); err != nil { return nil, fmt.Errorf("Error loading htpasswd file %s: %v", htpasswdFile, err) } else { return htpasswordAuth, nil } case (*configapi.BasicAuthPasswordIdentityProvider): connectionInfo := provider.RemoteConnectionInfo if len(connectionInfo.URL) == 0 { return nil, fmt.Errorf("URL is required for BasicAuthPasswordIdentityProvider") } transport, err := cmdutil.TransportFor(connectionInfo.CA, connectionInfo.ClientCert.CertFile, connectionInfo.ClientCert.KeyFile) if err != nil { return nil, fmt.Errorf("Error building BasicAuthPasswordIdentityProvider client: %v", err) } return basicauthpassword.New(identityProvider.Name, connectionInfo.URL, transport, identityMapper), nil default: return nil, fmt.Errorf("No password auth found that matches %v. The OAuth server cannot start!", identityProvider) } }
func (c *AuthConfig) getAuthenticationHandler(mux cmdutil.Mux, errorHandler handlers.AuthenticationErrorHandler) (handlers.AuthenticationHandler, error) { challengers := map[string]handlers.AuthenticationChallenger{} redirectors := map[string]handlers.AuthenticationRedirector{} for _, identityProvider := range c.Options.IdentityProviders { identityMapper := identitymapper.NewAlwaysCreateUserIdentityToUserMapper(c.IdentityRegistry, c.UserRegistry) if configapi.IsPasswordAuthenticator(identityProvider) { passwordAuth, err := c.getPasswordAuthenticator(identityProvider) if err != nil { return nil, err } if identityProvider.UseAsLogin { // Password auth requires: // 1. a session success handler (to remember you logged in) // 2. a redirectSuccessHandler (to go back to the "then" param) if c.SessionAuth == nil { return nil, errors.New("SessionAuth is required for password-based login") } passwordSuccessHandler := handlers.AuthenticationSuccessHandlers{c.SessionAuth, redirectSuccessHandler{}} redirectors["login"] = &redirector{RedirectURL: OpenShiftLoginPrefix, ThenParam: "then"} login := login.NewLogin(c.getCSRF(), &callbackPasswordAuthenticator{passwordAuth, passwordSuccessHandler}, login.DefaultLoginFormRenderer) login.Install(mux, OpenShiftLoginPrefix) } if identityProvider.UseAsChallenger { challengers["login"] = passwordchallenger.NewBasicAuthChallenger("openshift") } } else if configapi.IsOAuthIdentityProvider(identityProvider) { oauthProvider, err := c.getOAuthProvider(identityProvider) if err != nil { return nil, err } // Default state builder, combining CSRF and return URL handling state := external.CSRFRedirectingState(c.getCSRF()) // OAuth auth requires // 1. a session success handler (to remember you logged in) // 2. a state success handler (to go back to the URL encoded in the state) if c.SessionAuth == nil { return nil, errors.New("SessionAuth is required for OAuth-based login") } oauthSuccessHandler := handlers.AuthenticationSuccessHandlers{c.SessionAuth, state} // If the specified errorHandler doesn't handle the login error, let the state error handler attempt to propagate specific errors back to the token requester oauthErrorHandler := handlers.AuthenticationErrorHandlers{errorHandler, state} callbackPath := path.Join(OpenShiftOAuthCallbackPrefix, identityProvider.Name) oauthHandler, err := external.NewExternalOAuthRedirector(oauthProvider, state, c.Options.MasterPublicURL+callbackPath, oauthSuccessHandler, oauthErrorHandler, identityMapper) if err != nil { return nil, fmt.Errorf("unexpected error: %v", err) } mux.Handle(callbackPath, oauthHandler) if identityProvider.UseAsLogin { redirectors[identityProvider.Name] = oauthHandler } if identityProvider.UseAsChallenger { return nil, errors.New("oauth identity providers cannot issue challenges") } } } if len(redirectors) > 0 && len(challengers) == 0 { // Add a default challenger that will warn and give a link to the web browser token-granting location challengers["placeholder"] = placeholderchallenger.New(OpenShiftOAuthTokenRequestURL(c.Options.MasterPublicURL)) } authHandler := handlers.NewUnionAuthenticationHandler(challengers, redirectors, errorHandler) return authHandler, nil }