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

SSO: Link tokens rather than copying them #2916

Merged
merged 12 commits into from
Sep 10, 2018
21 changes: 1 addition & 20 deletions src/jetstream/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ func (p *portalProxy) DoLoginToCNSIwithConsoleUAAtoken(c echo.Context, theCNSIre
}

if uaaUrl.String() == p.GetConfig().ConsoleConfig.UAAEndpoint.String() { // CNSI UAA server matches Console UAA server
uaaToken.LinkedGUID = uaaToken.TokenGUID
err = p.setCNSITokenRecord(theCNSIrecord.GUID, u.UserGUID, uaaToken)
return err
} else {
Expand Down Expand Up @@ -688,26 +689,6 @@ func (p *portalProxy) InitEndpointTokenRecord(expiry int64, authTok string, refr
return tokenRecord
}

func (p *portalProxy) removed_saveCNSIToken(cnsiID string, u interfaces.JWTUserTokenInfo, authTok string, refreshTok string, disconnect bool) (interfaces.TokenRecord, error) {
log.Debug("saveCNSIToken")

tokenRecord := interfaces.TokenRecord{
AuthToken: authTok,
RefreshToken: refreshTok,
TokenExpiry: u.TokenExpiry,
Disconnected: disconnect,
AuthType: interfaces.AuthTypeOAuth2,
}

err := p.setCNSITokenRecord(cnsiID, u.UserGUID, tokenRecord)
if err != nil {
log.Errorf("%v", err)
return interfaces.TokenRecord{}, err
}

return tokenRecord, nil
}

func (p *portalProxy) deleteCNSIToken(cnsiID string, userGUID string) error {
log.Debug("deleteCNSIToken")

Expand Down
11 changes: 6 additions & 5 deletions src/jetstream/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
)

const (
findUAATokenSql = `SELECT auth_token, refresh_token, token_expiry, auth_type, meta_data FROM tokens .*`
findUAATokenSql = `SELECT token_guid, auth_token, refresh_token, token_expiry, auth_type, meta_data FROM tokens .*`
)

func TestLoginToUAA(t *testing.T) {
Expand Down Expand Up @@ -549,9 +549,10 @@ func TestVerifySession(t *testing.T) {
t.Error(errors.New("Unable to mock/stub user in session object."))
}

mockTokenGUID := "mock-token-guid"
encryptedUAAToken, _ := crypto.EncryptToken(pp.Config.EncryptionKeyInBytes, mockUAAToken)
expectedTokensRow := sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "auth_type", "meta_data"}).
AddRow(encryptedUAAToken, encryptedUAAToken, mockTokenExpiry, "oauth", "")
expectedTokensRow := sqlmock.NewRows([]string{"token_guid", "auth_token", "refresh_token", "token_expiry", "auth_type", "meta_data"}).
AddRow(mockTokenGUID, encryptedUAAToken, encryptedUAAToken, mockTokenExpiry, "oauth", "")

mock.ExpectQuery(selectAnyFromTokens).
WithArgs(mockUserGUID).
Expand All @@ -561,8 +562,8 @@ func TestVerifySession(t *testing.T) {
AddRow(mockProxyVersion)
mock.ExpectQuery(getDbVersion).WillReturnRows(expectVersionRow)

rs := sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "auth_type", "meta_data"}).
AddRow(encryptedUAAToken, encryptedUAAToken, mockTokenExpiry, "oauth", "")
rs := sqlmock.NewRows([]string{"token_guid", "auth_token", "refresh_token", "token_expiry", "auth_type", "meta_data"}).
AddRow(mockTokenGUID, encryptedUAAToken, encryptedUAAToken, mockTokenExpiry, "oauth", "")
mock.ExpectQuery(findUAATokenSql).
WillReturnRows(rs)

Expand Down
21 changes: 20 additions & 1 deletion src/jetstream/cnsi.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ func (p *portalProxy) GetCNSITokenRecord(cnsiGUID string, userGUID string) (inte
}

func (p *portalProxy) GetCNSITokenRecordWithDisconnected(cnsiGUID string, userGUID string) (interfaces.TokenRecord, bool) {
log.Debug("GetCNSITokenRecord")
log.Debug("GetCNSITokenRecordWithDisconnected")
tokenRepo, err := tokens.NewPgsqlTokenRepository(p.DatabaseConnectionPool)
if err != nil {
return interfaces.TokenRecord{}, false
Expand Down Expand Up @@ -380,6 +380,25 @@ func (p *portalProxy) ListEndpointsByUser(userGUID string) ([]*interfaces.Connec
return cnsiList, nil
}

// Uopdate the Access Token, Refresh Token and Token Expiry for a token
func (p *portalProxy) updateTokenAuth(userGUID string, t interfaces.TokenRecord) error {
log.Debug("updateTokenAuth")
tokenRepo, err := tokens.NewPgsqlTokenRepository(p.DatabaseConnectionPool)
if err != nil {
log.Errorf(dbReferenceError, err)
return fmt.Errorf(dbReferenceError, err)
}

err = tokenRepo.UpdateTokenAuth(userGUID, t, p.Config.EncryptionKeyInBytes)
if err != nil {
msg := "Unable to update Token: %v"
log.Errorf(msg, err)
return fmt.Errorf(msg, err)
}

return nil
}

func (p *portalProxy) setCNSITokenRecord(cnsiGUID string, userGUID string, t interfaces.TokenRecord) error {
log.Debug("setCNSITokenRecord")
tokenRepo, err := tokens.NewPgsqlTokenRepository(p.DatabaseConnectionPool)
Expand Down
42 changes: 42 additions & 0 deletions src/jetstream/datastore/20180824092600_LinkedTokens.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package datastore

import (
"database/sql"

"bitbucket.org/liamstask/goose/lib/goose"
)

func init() {
RegisterMigration(20180813110300, "LinkedTokens", func(txn *sql.Tx, conf *goose.DBConf) error {

addTokenID := "ALTER TABLE tokens ADD token_guid VARCHAR(36) DEFAULT 'default-token'"
_, err := txn.Exec(addTokenID)
if err != nil {
return err
}

addLinkedTokens := "ALTER TABLE tokens ADD linked_token VARCHAR(36)"
_, err = txn.Exec(addLinkedTokens)
if err != nil {
return err
}

// Ensure any existing tokens have an ID

// For UAA tokens, use the user id
ensureUAATokenID := "UPDATE tokens SET token_guid=user_guid WHERE token_guid IS NULL AND token_type='uaa'"
_, err = txn.Exec(ensureUAATokenID)
if err != nil {
return err
}

// For CNSI tokens, use the cnsi guid
ensureCNSITokenID := "UPDATE tokens SET token_guid=cnsi_guid WHERE token_guid IS NULL"
_, err = txn.Exec(ensureCNSITokenID)
if err != nil {
return err
}

return nil
})
}
9 changes: 5 additions & 4 deletions src/jetstream/mock_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const mockCFGUID = "some-cf-guid-1234"
const mockCEGUID = "some-hce-guid-1234"
const mockUserGUID = "asd-gjfg-bob"
const mockAdminGUID = tokens.SystemSharedUserGuid
const mockTokenGUID = "mock-token-guid"

const mockURLString = "http://localhost:9999/some/fake/url/"

Expand Down Expand Up @@ -170,15 +171,15 @@ func expectCFAndCERows() sqlmock.Rows {
}

func expectTokenRow() sqlmock.Rows {
return sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected", "auth_type", "meta_data", "user_guid"}).
AddRow(mockUAAToken, mockUAAToken, mockTokenExpiry, false, "OAuth2", "", mockUserGUID)
return sqlmock.NewRows([]string{"token_guid", "auth_token", "refresh_token", "token_expiry", "disconnected", "auth_type", "meta_data", "user_guid", "linked_token"}).
AddRow(mockTokenGUID, mockUAAToken, mockUAAToken, mockTokenExpiry, false, "OAuth2", "", mockUserGUID, nil)
}

func expectEncryptedTokenRow(mockEncryptionKey []byte) sqlmock.Rows {

encryptedUaaToken, _ := crypto.EncryptToken(mockEncryptionKey, mockUAAToken)
return sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected", "auth_type", "meta_data", "user_guid"}).
AddRow(encryptedUaaToken, encryptedUaaToken, mockTokenExpiry, false, "OAuth2", "", mockUserGUID)
return sqlmock.NewRows([]string{"token_guid", "auth_token", "refresh_token", "token_expiry", "disconnected", "auth_type", "meta_data", "user_guid", "linked_token"}).
AddRow(mockTokenGUID, encryptedUaaToken, encryptedUaaToken, mockTokenExpiry, false, "OAuth2", "", mockUserGUID, nil)
}

func setupHTTPTest(req *http.Request) (*httptest.ResponseRecorder, *echo.Echo, echo.Context, *portalProxy, *sql.DB, sqlmock.Sqlmock) {
Expand Down
5 changes: 3 additions & 2 deletions src/jetstream/oauth_requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,10 @@ func (p *portalProxy) RefreshOAuthToken(skipSSLValidation bool, cnsiGUID, userGU
u.UserGUID = userGUID

tokenRecord := p.InitEndpointTokenRecord(u.TokenExpiry, uaaRes.AccessToken, uaaRes.RefreshToken, userToken.Disconnected)
err = p.setCNSITokenRecord(cnsiGUID, userGUID, tokenRecord)
tokenRecord.TokenGUID = userToken.TokenGUID
err = p.updateTokenAuth(userGUID, tokenRecord)
if err != nil {
return t, fmt.Errorf("Couldn't save new token: %v", err)
return t, fmt.Errorf("Couldn't update token: %v", err)
}

return tokenRecord, nil
Expand Down
29 changes: 14 additions & 15 deletions src/jetstream/oauth_requests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ func TestDoOauthFlowRequestWithValidToken(t *testing.T) {
TokenExpiry: tokenExpiration,
}

mockTokenGUID := "mock-token-guid"

// set up the database expectation for pp.setCNSITokenRecord
mock.ExpectQuery(selectAnyFromTokens).
WithArgs(mockCNSIGUID, mockUserGUID).
Expand All @@ -97,8 +99,8 @@ func TestDoOauthFlowRequestWithValidToken(t *testing.T) {
// p.getCNSIRequestRecords(cnsiRequest) ->
// p.getCNSITokenRecord(r.GUID, r.UserGUID) ->
// tokenRepo.FindCNSIToken(cnsiGUID, userGUID)
expectedCNSITokenRow := sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected", "auth_type", "meta_data", "user_guid"}).
AddRow(encryptedToken, encryptedToken, tokenExpiration, false, "OAuth2", "", mockUserGUID)
expectedCNSITokenRow := sqlmock.NewRows([]string{"token_guid", "auth_token", "refresh_token", "token_expiry", "disconnected", "auth_type", "meta_data", "user_guid", "linked_token"}).
AddRow(mockTokenGUID, encryptedToken, encryptedToken, tokenExpiration, false, "OAuth2", "", mockUserGUID, nil)
mock.ExpectQuery(selectAnyFromTokens).
WithArgs(mockCNSIGUID, mockUserGUID, mockAdminGUID).
WillReturnRows(expectedCNSITokenRow)
Expand Down Expand Up @@ -204,6 +206,8 @@ func TestDoOauthFlowRequestWithExpiredToken(t *testing.T) {
TokenExpiry: tokenExpiration,
}

mockTokenGUID := "mock-token-guid"

_, _, _, pp, db, mock := setupHTTPTest(req)
defer db.Close()
encryptedUAAToken, _ := crypto.EncryptToken(pp.Config.EncryptionKeyInBytes, mockUAAToken)
Expand All @@ -227,8 +231,8 @@ func TestDoOauthFlowRequestWithExpiredToken(t *testing.T) {
// p.getCNSIRequestRecords(cnsiRequest) ->
// p.getCNSITokenRecord(r.GUID, r.UserGUID) ->
// tokenRepo.FindCNSIToken(cnsiGUID, userGUID)
expectedCNSITokenRow := sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected", "auth_type", "meta_data", "user_guid"}).
AddRow(encryptedUAAToken, encryptedUAAToken, tokenExpiration, false, "OAuth2", "", mockUserGUID)
expectedCNSITokenRow := sqlmock.NewRows([]string{"token_guid", "auth_token", "refresh_token", "token_expiry", "disconnected", "auth_type", "meta_data", "user_guid", "linked_token"}).
AddRow(mockTokenGUID, encryptedUAAToken, encryptedUAAToken, tokenExpiration, false, "OAuth2", "", mockUserGUID, nil)
mock.ExpectQuery(selectAnyFromTokens).
WithArgs(mockCNSIGUID, mockUserGUID, mockAdminGUID).
WillReturnRows(expectedCNSITokenRow)
Expand All @@ -240,19 +244,14 @@ func TestDoOauthFlowRequestWithExpiredToken(t *testing.T) {
WithArgs(mockCNSIGUID).
WillReturnRows(expectedCNSIRecordRow)

expectedCNSITokenRecordRow := sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected", "auth_type", "meta_data", "user_guid"}).
AddRow(encryptedUAAToken, encryptedUAAToken, tokenExpiration, false, "OAuth2", "", mockUserGUID)
expectedCNSITokenRecordRow := sqlmock.NewRows([]string{"token_guid", "auth_token", "refresh_token", "token_expiry", "disconnected", "auth_type", "meta_data", "user_guid", "linked_token"}).
AddRow(mockTokenGUID, encryptedUAAToken, encryptedUAAToken, tokenExpiration, false, "OAuth2", "", mockUserGUID, nil)
mock.ExpectQuery(selectAnyFromTokens).
WithArgs(mockCNSIGUID, mockUserGUID, mockAdminGUID).
WillReturnRows(expectedCNSITokenRecordRow)

mock.ExpectQuery(selectAnyFromTokens).
WithArgs(mockCNSIGUID, mockUserGUID).
WillReturnRows(sqlmock.NewRows([]string{"COUNT(*)"}).AddRow("0"))

// Expect the INSERT
mock.ExpectExec(insertIntoTokens).
//WithArgs(mockCNSIGUID, mockUserGUID, "cnsi", encryptedUAAToken, encryptedUAAToken, mockTokenRecord.TokenExpiry).
// A token refresh attempt will be made - which is just an update
mock.ExpectExec(updateTokens).
WillReturnResult(sqlmock.NewResult(1, 1))

//
Expand Down Expand Up @@ -370,8 +369,8 @@ func TestDoOauthFlowRequestWithFailedRefreshMethod(t *testing.T) {
// p.getCNSIRequestRecords(cnsiRequest) ->
// p.getCNSITokenRecord(r.GUID, r.UserGUID) ->
// tokenRepo.FindCNSIToken(cnsiGUID, userGUID)
expectedCNSITokenRow := sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected", "auth_type", "meta_data", "user_guid"}).
AddRow(encryptedUAAToken, encryptedUAAToken, tokenExpiration, false, "OAuth2", "", mockUserGUID)
expectedCNSITokenRow := sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected", "auth_type", "meta_data", "user_guid", "linked_token"}).
AddRow(encryptedUAAToken, encryptedUAAToken, tokenExpiration, false, "OAuth2", "", mockUserGUID, nil)
mock.ExpectQuery(selectAnyFromTokens).
WithArgs(mockCNSIGUID, mockUserGUID, mockAdminGUID).
WillReturnRows(expectedCNSITokenRow)
Expand Down
2 changes: 1 addition & 1 deletion src/jetstream/passthrough.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ func (p *portalProxy) ProxyRequest(c echo.Context, uri *url.URL) (map[string]*in

if shouldPassthrough {
if len(cnsiList) > 1 {
err := errors.New("Requested passthrough to multiple CNSIs. Only single CNSI passthroughs are supported.")
err := errors.New("Requested passthrough to multiple CNSIs. Only single CNSI passthroughs are supported")
return nil, echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/jetstream/repository/interfaces/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,17 @@ type EndpointTokenRecord struct {
LoggingEndpoint string
}

//TODO this could be moved back to tokens subpackage, and extensions could import it?
// TokenRecord repsrents and endpoint or uaa token
type TokenRecord struct {
TokenGUID string
AuthToken string
RefreshToken string
TokenExpiry int64
Disconnected bool
AuthType string
Metadata string
SystemShared bool
LinkedGUID string // Indicates the GUID of the token that this token is linked to (if any)
}

type CFInfo struct {
Expand Down
Loading