<commit_before>package tokenrequest

import (
	"fmt"
	"html/template"
	"io"
	"net/http"
	"path"
	"sync"

	"github.com/RangelReale/osincli"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"

	"github.com/openshift/client-go/oauth/clientset/versioned/typed/oauth/v1"
	"github.com/openshift/origin/pkg/oauth/urls"
	"github.com/openshift/origin/pkg/oauthserver"
	"github.com/openshift/origin/pkg/oauthserver/authenticator/password/bootstrap"
)

type tokenRequest struct {
	publicMasterURL string
	// osinOAuthClient is the private OAuth client used by this endpoint.
	// It starts out nil and is lazily initialized when this endpoint is called.
	osinOAuthClient *osincli.Client
	// osinOAuthClientGetter is used to initialize osinOAuthClient.
	// Since it can return an error, it may be called multiple times.
	osinOAuthClientGetter func() (*osincli.Client, error)
	// ready is closed to signal that osinOAuthClient is no longer nil.
	// Nothing sends on ready so <-ready only returns when it has been closed.
	ready chan struct{}
	// initLock guards reads and writes to osinOAuthClient when it could still be nil.
	initLock sync.Mutex

	// to check if we need the logout link for the bootstrap user
	tokens                v1.OAuthAccessTokenInterface
	openShiftLogoutPrefix string
}

func NewTokenRequest(publicMasterURL, openShiftLogoutPrefix string, osinOAuthClientGetter func() (*osincli.Client, error), tokens v1.OAuthAccessTokenInterface) oauthserver.Endpoints {
	return &tokenRequest{
		publicMasterURL:       publicMasterURL,
		osinOAuthClientGetter: osinOAuthClientGetter,
		ready:                 make(chan struct{}),
		tokens:                tokens,
		openShiftLogoutPrefix: openShiftLogoutPrefix,
	}
}

func (t *tokenRequest) Install(mux oauthserver.Mux, prefix string) {
	mux.HandleFunc(path.Join(prefix, urls.RequestTokenEndpoint), t.readyHandler(t.requestToken))
	mux.HandleFunc(path.Join(prefix, urls.DisplayTokenEndpoint), t.readyHandler(t.displayToken))
	mux.HandleFunc(path.Join(prefix, urls.ImplicitTokenEndpoint), t.implicitToken)
}

// TODO we may want to start doing live lookups for this endpoint
func (t *tokenRequest) readyHandler(delegate func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, h *http.Request) {
		select {
		case <-t.ready:
		default:
			if err := t.safeInitOsinOAuthClientOnce(); err != nil {
				utilruntime.HandleError(fmt.Errorf("failed to get Osin OAuth client for token endpoint: %v", err))
				http.Error(w, "OAuth token endpoint is not ready", http.StatusInternalServerError)
				return
			}
		}
		delegate(w, h)
	}
}

// safeInitOsinOAuthClientOnce initializes osinOAuthClient exactly once using osinOAuthClientGetter.
// It is goroutine safe, reentrant and can be safely called multiple times.
func (t *tokenRequest) safeInitOsinOAuthClientOnce() error {
	// Use a lock and nil check to make sure we never close endpoints.ready more than once
	// and that we only try to fetch osinOAuthClient until the first time we are successful
	t.initLock.Lock()
	defer t.initLock.Unlock()
	if t.osinOAuthClient == nil {
		osinOAuthClient, err := t.osinOAuthClientGetter()
		if err != nil {
			return err
		}
		t.osinOAuthClient = osinOAuthClient
		close(t.ready)
	}
	return nil
}

// requestToken works for getting a token in your browser and seeing what your token is
func (t *tokenRequest) requestToken(w http.ResponseWriter, req *http.Request) {
	authReq := t.osinOAuthClient.NewAuthorizeRequest(osincli.CODE)
	oauthURL := authReq.GetAuthorizeUrl()

	http.Redirect(w, req, oauthURL.String(), http.StatusFound)
}

func (t *tokenRequest) displayToken(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "text/html; charset=UTF-8")
	requestURL := urls.OpenShiftOAuthTokenRequestURL("") // relative url to token request endpoint
	data := tokenData{RequestURL: requestURL, PublicMasterURL: t.publicMasterURL}

	authorizeReq := t.osinOAuthClient.NewAuthorizeRequest(osincli.CODE)
	authorizeData, err := authorizeReq.HandleRequest(req)
	if err != nil {
		data.Error = fmt.Sprintf("Error handling auth request: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		renderToken(w, data)
		return
	}

	accessReq := t.osinOAuthClient.NewAccessRequest(osincli.AUTHORIZATION_CODE, authorizeData)
	accessData, err := accessReq.GetToken()
	if err != nil {
		data.Error = fmt.Sprintf("Error getting token: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		renderToken(w, data)
		return
	}

	token, err := t.tokens.Get(accessData.AccessToken, metav1.GetOptions{})
	if err != nil {
		data.Error = "Error checking token" // do not leak error to user, do not log error
		w.WriteHeader(http.StatusInternalServerError)
		renderToken(w, data)
		return
	}

	if token.UserName == bootstrap.BootstrapUser {
		// only the bootstrap user has a session we maintain for one more than OAuth flow
		data.LogoutURL = t.openShiftLogoutPrefix
	}

	data.AccessToken = accessData.AccessToken
	renderToken(w, data)
}

func renderToken(w io.Writer, data tokenData) {
	if err := tokenTemplate.Execute(w, data); err != nil {
		utilruntime.HandleError(fmt.Errorf("unable to render token template: %v", err))
	}<commit_msg>Remove token request OAuth client cache<commit_after>package tokenrequest

import (
	"fmt"
	"html/template"
	"io"
	"net/http"
	"path"

	"github.com/RangelReale/osincli"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"

	"github.com/openshift/client-go/oauth/clientset/versioned/typed/oauth/v1"
	"github.com/openshift/origin/pkg/oauth/urls"
	"github.com/openshift/origin/pkg/oauthserver"
	"github.com/openshift/origin/pkg/oauthserver/authenticator/password/bootstrap"
)

type tokenRequest struct {
	publicMasterURL string
	// osinOAuthClientGetter is used to initialize osinOAuthClient.
	// Since it can return an error, it may be called multiple times.
	osinOAuthClientGetter func() (*osincli.Client, error)

	// to check if we need the logout link for the bootstrap user
	tokens                v1.OAuthAccessTokenInterface
	openShiftLogoutPrefix string
}

func NewTokenRequest(publicMasterURL, openShiftLogoutPrefix string, osinOAuthClientGetter func() (*osincli.Client, error), tokens v1.OAuthAccessTokenInterface) oauthserver.Endpoints {
	return &tokenRequest{
		publicMasterURL:       publicMasterURL,
		osinOAuthClientGetter: osinOAuthClientGetter,
		tokens:                tokens,
		openShiftLogoutPrefix: openShiftLogoutPrefix,
	}
}

func (t *tokenRequest) Install(mux oauthserver.Mux, prefix string) {
	mux.HandleFunc(path.Join(prefix, urls.RequestTokenEndpoint), t.oauthClientHandler(t.requestToken))
	mux.HandleFunc(path.Join(prefix, urls.DisplayTokenEndpoint), t.oauthClientHandler(t.displayToken))
	mux.HandleFunc(path.Join(prefix, urls.ImplicitTokenEndpoint), t.implicitToken)
}

func (t *tokenRequest) oauthClientHandler(delegate func(*osincli.Client, http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, h *http.Request) {
		osinOAuthClient, err := t.osinOAuthClientGetter()
		if err != nil {
			utilruntime.HandleError(fmt.Errorf("failed to get Osin OAuth client for token endpoint: %v", err))
			http.Error(w, "OAuth token endpoint is not ready", http.StatusInternalServerError)
			return
		}
		delegate(osinOAuthClient, w, h)
	}
}

// requestToken works for getting a token in your browser and seeing what your token is
func (t *tokenRequest) requestToken(osinOAuthClient *osincli.Client, w http.ResponseWriter, req *http.Request) {
	authReq := osinOAuthClient.NewAuthorizeRequest(osincli.CODE)
	oauthURL := authReq.GetAuthorizeUrl()

	http.Redirect(w, req, oauthURL.String(), http.StatusFound)
}

func (t *tokenRequest) displayToken(osinOAuthClient *osincli.Client, w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "text/html; charset=UTF-8")
	requestURL := urls.OpenShiftOAuthTokenRequestURL("") // relative url to token request endpoint
	data := tokenData{RequestURL: requestURL, PublicMasterURL: t.publicMasterURL}

	authorizeReq := osinOAuthClient.NewAuthorizeRequest(osincli.CODE)
	authorizeData, err := authorizeReq.HandleRequest(req)
	if err != nil {
		data.Error = fmt.Sprintf("Error handling auth request: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		renderToken(w, data)
		return
	}

	accessReq := osinOAuthClient.NewAccessRequest(osincli.AUTHORIZATION_CODE, authorizeData)
	accessData, err := accessReq.GetToken()
	if err != nil {
		data.Error = fmt.Sprintf("Error getting token: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		renderToken(w, data)
		return
	}

	token, err := t.tokens.Get(accessData.AccessToken, metav1.GetOptions{})
	if err != nil {
		data.Error = "Error checking token" // do not leak error to user, do not log error
		w.WriteHeader(http.StatusInternalServerError)
		renderToken(w, data)
		return
	}

	if token.UserName == bootstrap.BootstrapUser {
		// only the bootstrap user has a session we maintain for one more than OAuth flow
		data.LogoutURL = t.openShiftLogoutPrefix
	}

	data.AccessToken = accessData.AccessToken
	renderToken(w, data)
}

func renderToken(w io.Writer, data tokenData) {
	if err := tokenTemplate.Execute(w, data); err != nil {
		utilruntime.HandleError(fmt.Errorf("unable to render token template: %v", err))
	}
{'fraction_non_alphanumeric': 0.10442065090639245, 'fraction_numerical': 0.001908194635852857, 'mean_word_length': 4.881546134663342, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1785118', 'n_tokens_mistral': 3145, 'n_tokens_neox': 3004, 'n_words': 763}