Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CF Push: A better fix for the token expiry issue #4289

Merged
merged 1 commit into from
May 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/jetstream/plugins/cfapppush/connection_wrapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package cfapppush

import (
"time"

"code.cloudfoundry.org/cli/api/cloudcontroller"
"code.cloudfoundry.org/cli/command"

"github.com/cloudfoundry-incubator/stratos/src/jetstream/repository/interfaces"
)

// PushConnectionWrapper can wrap a given connection allowing the wrapper to modify
// all requests going in and out of the given connection.
type PushConnectionWrapper struct {
inner cloudcontroller.Connection
portalProxy interfaces.PortalProxy
config *CFPushAppConfig
cmdConfig command.Config
}

// Wrap an existing connection
func (cw PushConnectionWrapper) Wrap(innerconnection cloudcontroller.Connection) cloudcontroller.Connection {
cw.inner = innerconnection
return cw
}

// Make makes an HTTP request
func (cw PushConnectionWrapper) Make(request *cloudcontroller.Request, passedResponse *cloudcontroller.Response) error {
// Check to see if the token is about to expire, if it is, refresh it first
token, found := cw.portalProxy.GetCNSITokenRecord(cw.config.EndpointID, cw.config.UserID)
if found {
// Aways update the access token, in case someone else refreshed it
cw.config.AuthToken = token.AuthToken

// Check if this is about to expire in the next 30 seconds
expiry := token.TokenExpiry - 30
expTime := time.Unix(expiry, 0)
if expTime.Before(time.Now()) {
cnsiRecord, err := cw.portalProxy.GetCNSIRecord(cw.config.EndpointID)
if err == nil {
// Refresh token first - makes sure it will be valid when we do the push
refreshedTokenRec, err := cw.portalProxy.RefreshOAuthToken(cnsiRecord.SkipSSLValidation, cnsiRecord.GUID, cw.config.UserID, cnsiRecord.ClientId, cnsiRecord.ClientSecret, cnsiRecord.TokenEndpoint)
if err == nil {
cw.config.AuthToken = refreshedTokenRec.AuthToken
}
}
}
}

cw.cmdConfig.SetAccessToken("bearer " + cw.config.AuthToken)

return cw.inner.Make(request, passedResponse)
}
24 changes: 10 additions & 14 deletions src/jetstream/plugins/cfapppush/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ func (cfAppPush *CFAppPush) deploy(echoContext echo.Context) error {
// App ID is this is a redeploy
appID := echoContext.QueryParam("app")

log.Debug("UpgradeToWebSocket")
clientWebSocket, pingTicker, err := interfaces.UpgradeToWebSocket(echoContext)
log.Debug("UpgradeToWebSocket done")
if err != nil {
log.Errorf("Upgrade to websocket failed due to: %+v", err)
return err
Expand Down Expand Up @@ -197,9 +199,9 @@ func (cfAppPush *CFAppPush) deploy(echoContext echo.Context) error {
pushConfig.DialTimeout = dialTimeout

// Initialise Push Command
cfAppPush.cfPush = Constructor(pushConfig, cfAppPush.portalProxy)
cfPush := Constructor(pushConfig, cfAppPush.portalProxy)

err = cfAppPush.cfPush.Init(appDir, manifestFile, overrides)
err = cfPush.Init(appDir, manifestFile, overrides)
if err != nil {
log.Warnf("Failed to parse due to: %+v", err)
sendErrorMessage(clientWebSocket, err, CLOSE_FAILURE)
Expand All @@ -208,7 +210,7 @@ func (cfAppPush *CFAppPush) deploy(echoContext echo.Context) error {

sendEvent(clientWebSocket, EVENT_PUSH_STARTED)

err = cfAppPush.cfPush.Run(cfAppPush, clientWebSocket)
err = cfPush.Run(cfAppPush, clientWebSocket)
if err != nil {
log.Warnf("Failed to execute due to: %+v", err)
sendErrorMessage(clientWebSocket, err, CLOSE_PUSH_ERROR)
Expand Down Expand Up @@ -479,34 +481,28 @@ func (cfAppPush *CFAppPush) getConfigData(echoContext echo.Context, cnsiGUID str
sendErrorMessage(clientWebSocket, err, CLOSE_NO_SESSION)
return nil, err
}
_, found := cfAppPush.portalProxy.GetCNSITokenRecord(cnsiGUID, userID)

token, found := cfAppPush.portalProxy.GetCNSITokenRecord(cnsiGUID, userID)
if !found {
log.Warnf("Failed to retrieve record for CNSI %s", cnsiGUID)
sendErrorMessage(clientWebSocket, err, CLOSE_NO_CNSI_USERTOKEN)
return nil, errors.New("Failed to find token record")
}

// Refresh token first - makes sure it will be valid when we do the push
refreshedTokenRec, err := cfAppPush.portalProxy.RefreshOAuthToken(cnsiRecord.SkipSSLValidation, cnsiRecord.GUID, userID, cnsiRecord.ClientId, cnsiRecord.ClientSecret, cnsiRecord.TokenEndpoint)
if err != nil {
log.Warnf("Couldn't get refresh token for endpoint with GUID %s", cnsiRecord.GUID)
sendErrorMessage(clientWebSocket, err, CLOSE_NO_CNSI_USERTOKEN)
return nil, fmt.Errorf("Couldn't get refresh token for endpoint with GUID %s", cnsiRecord.GUID)
}

config := &CFPushAppConfig{
AuthorizationEndpoint: cnsiRecord.AuthorizationEndpoint,
CFClient: cnsiRecord.ClientId,
CFClientSecret: cnsiRecord.ClientSecret,
APIEndpointURL: cnsiRecord.APIEndpoint.String(),
DopplerLoggingEndpoint: cnsiRecord.DopplerLoggingEndpoint,
SkipSSLValidation: cnsiRecord.SkipSSLValidation,
AuthToken: refreshedTokenRec.AuthToken,
RefreshToken: refreshedTokenRec.RefreshToken,
AuthToken: token.AuthToken,
OrgGUID: orgGUID,
OrgName: orgName,
SpaceGUID: spaceGUID,
SpaceName: spaceName,
EndpointID: cnsiGUID,
UserID: userID,
}

return config, nil
Expand Down
1 change: 1 addition & 0 deletions src/jetstream/plugins/cfapppush/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func (c *CFPushApp) setEndpointInfo(config *configv3.Config) error {
// Got the info we need - update the config with it
config.SetTargetInformation(apiEndpoint, info.APIVersion, info.AuthorizationEndpoint, info.MinCLIVersion, info.DopplerLoggingEndpoint, info.RoutingEndpoint, skipSSLValidation)
config.SetAccessToken("bearer " + c.config.AuthToken)
// Note: We do not give the refresh token to the CLI code as we do NOT want it to refresh the token
} else {
return errors.New("Did not get a CF /v2/info response")
}
Expand Down
1 change: 0 additions & 1 deletion src/jetstream/plugins/cfapppush/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
// CFAppPush is a plugin to allow applications to be pushed to Cloud Foundry from Stratos
type CFAppPush struct {
portalProxy interfaces.PortalProxy
cfPush CFPush
}

// Init creates a new CFAppPush
Expand Down
16 changes: 13 additions & 3 deletions src/jetstream/plugins/cfapppush/pushapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,14 @@ type CFPushAppConfig struct {
DopplerLoggingEndpoint string
SkipSSLValidation bool
AuthToken string
RefreshToken string
OrgGUID string
OrgName string
SpaceGUID string
SpaceName string
OutputWriter io.Writer
DialTimeout string
EndpointID string
UserID string
}

// CFPushAppOverrides represents the document that can be sent from the client with the app overrrides for the push
Expand Down Expand Up @@ -308,8 +309,8 @@ func (c *CFPushApp) Run(msgSender DeployAppMessageSender, clientWebsocket *webso
return nil
}

func (push *CFPushApp) setup(config command.Config, ui command.UI, msgSender DeployAppMessageSender, clientWebsocket *websocket.Conn) error {
cmd := push.pushCommand
func (c *CFPushApp) setup(config command.Config, ui command.UI, msgSender DeployAppMessageSender, clientWebsocket *websocket.Conn) error {
cmd := c.pushCommand
cmd.UI = ui
cmd.Config = config
sharedActor := sharedaction.NewActor(config)
Expand All @@ -320,6 +321,15 @@ func (push *CFPushApp) setup(config command.Config, ui command.UI, msgSender Dep
return err
}

// Initialize connection wrapper that will refresh the auto token if needed
pushConnectionWrapper := PushConnectionWrapper{
portalProxy: c.portalProxy,
config: c.config,
cmdConfig: config,
}

ccClient.WrapConnection(pushConnectionWrapper)

ccClientV3, _, err := sharedV3.NewV3BasedClients(config, ui, true)
if err != nil {
return err
Expand Down