Skip to content

Commit

Permalink
Merge pull request #1238 from SUSE/auto-register-cf
Browse files Browse the repository at this point in the history
Automatically register a predefined CF and attempt to connect on log in
  • Loading branch information
nwmac authored Oct 27, 2017
2 parents d28dcd2 + ae502dd commit bef83de
Show file tree
Hide file tree
Showing 20 changed files with 246 additions and 111 deletions.
1 change: 1 addition & 0 deletions .cfignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ dev-certs/
out/
outputs/
tmp/
tools/
CHANGELOG.md
test/
deploy/all-in-one/
Expand Down
34 changes: 28 additions & 6 deletions components/app-core/backend/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func (p *portalProxy) DoLoginToCNSI(c echo.Context, cnsiGUID string) (*interface
}
u.UserGUID = userID

p.saveCNSIToken(cnsiGUID, *u, uaaRes.AccessToken, uaaRes.RefreshToken)
p.saveCNSIToken(cnsiGUID, *u, uaaRes.AccessToken, uaaRes.RefreshToken, false)

cfAdmin := strings.Contains(uaaRes.Scope, p.Config.CFAdminIdentifier)

Expand Down Expand Up @@ -255,12 +255,33 @@ func (p *portalProxy) logoutOfCNSI(c echo.Context) error {
"Need CNSI GUID passed as form param")
}

userID, err := p.GetSessionStringValue(c, "user_id")
userGUID, err := p.GetSessionStringValue(c, "user_id")
if err != nil {
return echo.NewHTTPError(http.StatusUnauthorized, "Could not find correct session value")
return fmt.Errorf("Could not find correct session value: %s", err)
}

p.deleteCNSIToken(cnsiGUID, userID)
cnsiRecord, err := p.GetCNSIRecord(cnsiGUID)
if err != nil {
return fmt.Errorf("Unable to load CNSI record: %s", err)
}

// If cnsi is cf AND cf is auto-register only clear the entry
if cnsiRecord.CNSIType == "cf" && p.GetConfig().AutoRegisterCFUrl == cnsiRecord.APIEndpoint.String() {
log.Debug("Setting token record as disconnected")

userTokenInfo := userTokenInfo{
UserGUID: userGUID,
}

if _, err := p.saveCNSIToken(cnsiGUID, userTokenInfo, "cleared_token", "cleared_token", true); err != nil {
return fmt.Errorf("Unable to clear token: %s", err)
}
} else {
log.Debug("Deleting Token")
if err := p.deleteCNSIToken(cnsiGUID, userGUID); err != nil {
return fmt.Errorf("Unable to delete token: %s", err)
}
}

return nil
}
Expand Down Expand Up @@ -401,13 +422,14 @@ func (p *portalProxy) saveUAAToken(u userTokenInfo, authTok string, refreshTok s
return tokenRecord, nil
}

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

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

err := p.setCNSITokenRecord(cnsiID, u.UserGUID, tokenRecord)
Expand Down Expand Up @@ -614,7 +636,7 @@ func (p *portalProxy) GetCNSIUser(cnsiGUID string, userGUID string) (*interfaces
// get the scope out of the JWT token data
userTokenInfo, err := getUserTokenInfo(cfTokenRecord.AuthToken)
if err != nil {
msg := "Unable to find scope information in the UAA Auth Token: %s"
msg := "Unable to find scope information in the CNSI UAA Auth Token: %s"
log.Errorf(msg, err)
return nil, false
}
Expand Down
8 changes: 4 additions & 4 deletions components/app-core/backend/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ func TestSaveCNSITokenWithInvalidInput(t *testing.T) {

mock.ExpectExec(insertIntoTokens).
WillReturnError(errors.New("Unknown Database Error"))
tr, err := pp.saveCNSIToken(badCNSIID, badUserInfo, badAuthToken, badRefreshToken)
tr, err := pp.saveCNSIToken(badCNSIID, badUserInfo, badAuthToken, badRefreshToken, false)

log.Printf("tr is: %T %+v", tr, tr)
log.Printf("emptyTokenRecord is: %T %+v", emptyTokenRecord, emptyTokenRecord)
Expand Down Expand Up @@ -648,7 +648,7 @@ func TestVerifySessionExpired(t *testing.T) {
sessionValues["exp"] = time.Now().Add(-time.Hour).Unix()

mock.ExpectQuery(selectAnyFromTokens).
WillReturnRows(sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry"}))
WillReturnRows(sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected"}))
mock.ExpectExec(insertIntoTokens).
WillReturnError(errors.New("Session has expired"))

Expand All @@ -657,8 +657,8 @@ func TestVerifySessionExpired(t *testing.T) {
}

mock.ExpectQuery(selectAnyFromTokens).
WillReturnRows(sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry"}).
AddRow(mockUAAToken, mockUAAToken, sessionValues["exp"]))
WillReturnRows(sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected"}).
AddRow(mockUAAToken, mockUAAToken, sessionValues["exp"], false))
err := pp.verifySession(ctx)

Convey("Should fail to verify session", func() {
Expand Down
15 changes: 15 additions & 0 deletions components/app-core/backend/cnsi.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,21 @@ func (p *portalProxy) GetCNSITokenRecord(cnsiGUID string, userGUID string) (inte
return tr, true
}

func (p *portalProxy) GetCNSITokenRecordWithDisconnected(cnsiGUID string, userGUID string) (interfaces.TokenRecord, bool) {
log.Debug("GetCNSITokenRecord")
tokenRepo, err := tokens.NewPgsqlTokenRepository(p.DatabaseConnectionPool)
if err != nil {
return interfaces.TokenRecord{}, false
}

tr, err := tokenRepo.FindCNSITokenIncludeDisconnected(cnsiGUID, userGUID, p.Config.EncryptionKeyInBytes)
if err != nil {
return interfaces.TokenRecord{}, false
}

return tr, true
}

//TODO: remove this? It is unusable in this form as we won't know for which CNSI each token is
func (p *portalProxy) listCNSITokenRecordsForUser(userGUID string) ([]*interfaces.TokenRecord, error) {
log.Debug("listCNSITokenRecordsForUser")
Expand Down
1 change: 0 additions & 1 deletion components/app-core/backend/datastore/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,6 @@ func Ping(db *sql.DB) error {
// e.g Postgres uses $1, $2 etc
// SQLite uses ?
func ModifySQLStatement(sql string, databaseProvider string) string {

if databaseProvider == SQLITE || databaseProvider == MYSQL {
sqlParamReplace := regexp.MustCompile("\\$[0-9]")
return sqlParamReplace.ReplaceAllString(sql, "?")
Expand Down
8 changes: 4 additions & 4 deletions components/app-core/backend/mock_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,15 @@ func expectCFAndCERows() sqlmock.Rows {
}

func expectTokenRow() sqlmock.Rows {
return sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry"}).
AddRow(mockUAAToken, mockUAAToken, mockTokenExpiry)
return sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected"}).
AddRow(mockUAAToken, mockUAAToken, mockTokenExpiry, false)
}

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

encryptedUaaToken, _ := crypto.EncryptToken(mockEncryptionKey, mockUAAToken)
return sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry"}).
AddRow(encryptedUaaToken, encryptedUaaToken, mockTokenExpiry)
return sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected"}).
AddRow(encryptedUaaToken, encryptedUaaToken, mockTokenExpiry, false)
}

func setupHTTPTest(req *http.Request) (*httptest.ResponseRecorder, *echo.Echo, echo.Context, *portalProxy, *sql.DB, sqlmock.Sqlmock) {
Expand Down
4 changes: 2 additions & 2 deletions components/app-core/backend/oauth_requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (p *portalProxy) RefreshToken(skipSSLValidation bool, cnsiGUID, userGUID, c
log.Debug("refreshToken")
tokenEndpointWithPath := fmt.Sprintf("%s/oauth/token", tokenEndpoint)

userToken, ok := p.GetCNSITokenRecord(cnsiGUID, userGUID)
userToken, ok := p.GetCNSITokenRecordWithDisconnected(cnsiGUID, userGUID)
if !ok {
return t, fmt.Errorf("Info could not be found for user with GUID %s", userGUID)
}
Expand All @@ -98,7 +98,7 @@ func (p *portalProxy) RefreshToken(skipSSLValidation bool, cnsiGUID, userGUID, c

u.UserGUID = userGUID

t, err = p.saveCNSIToken(cnsiGUID, *u, uaaRes.AccessToken, uaaRes.RefreshToken)
t, err = p.saveCNSIToken(cnsiGUID, *u, uaaRes.AccessToken, uaaRes.RefreshToken, userToken.Disconnected)
if err != nil {
return t, fmt.Errorf("Couldn't save new token: %v", err)
}
Expand Down
24 changes: 12 additions & 12 deletions components/app-core/backend/oauth_requests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,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"}).
AddRow(encryptedToken, encryptedToken, tokenExpiration)
expectedCNSITokenRow := sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected"}).
AddRow(encryptedToken, encryptedToken, tokenExpiration, false)
mock.ExpectQuery(selectAnyFromTokens).
WithArgs(mockCNSIGUID, mockUserGUID).
WillReturnRows(expectedCNSITokenRow)
Expand Down Expand Up @@ -227,8 +227,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"}).
AddRow(encryptedUAAToken, encryptedUAAToken, tokenExpiration)
expectedCNSITokenRow := sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected"}).
AddRow(encryptedUAAToken, encryptedUAAToken, tokenExpiration, false)
mock.ExpectQuery(selectAnyFromTokens).
WithArgs(mockCNSIGUID, mockUserGUID).
WillReturnRows(expectedCNSITokenRow)
Expand All @@ -240,8 +240,8 @@ func TestDoOauthFlowRequestWithExpiredToken(t *testing.T) {
WithArgs(mockCNSIGUID).
WillReturnRows(expectedCNSIRecordRow)

expectedCNSITokenRecordRow := sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry"}).
AddRow(encryptedUAAToken, encryptedUAAToken, tokenExpiration)
expectedCNSITokenRecordRow := sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected"}).
AddRow(encryptedUAAToken, encryptedUAAToken, tokenExpiration, false)
mock.ExpectQuery(selectAnyFromTokens).
WithArgs(mockCNSIGUID, mockUserGUID).
WillReturnRows(expectedCNSITokenRecordRow)
Expand Down Expand Up @@ -370,8 +370,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"}).
AddRow(encryptedUAAToken, encryptedUAAToken, tokenExpiration)
expectedCNSITokenRow := sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected"}).
AddRow(encryptedUAAToken, encryptedUAAToken, tokenExpiration, false)
mock.ExpectQuery(selectAnyFromTokens).
WithArgs(mockCNSIGUID, mockUserGUID).
WillReturnRows(expectedCNSITokenRow)
Expand Down Expand Up @@ -614,8 +614,8 @@ func TestRefreshTokenWithDatabaseErrorOnSave(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"}).
AddRow(mockUAAToken, mockUAAToken, tokenExpiration)
expectedCNSITokenRow := sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected"}).
AddRow(mockUAAToken, mockUAAToken, tokenExpiration, false)
mock.ExpectQuery(selectAnyFromTokens).
WithArgs(mockCNSIGUID, mockUserGUID).
WillReturnRows(expectedCNSITokenRow)
Expand All @@ -627,8 +627,8 @@ func TestRefreshTokenWithDatabaseErrorOnSave(t *testing.T) {
WithArgs(mockCNSIGUID).
WillReturnRows(expectedCNSIRecordRow)

expectedCNSITokenRecordRow := sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry"}).
AddRow(mockUAAToken, mockUAAToken, tokenExpiration)
expectedCNSITokenRecordRow := sqlmock.NewRows([]string{"auth_token", "refresh_token", "token_expiry", "disconnected"}).
AddRow(mockUAAToken, mockUAAToken, tokenExpiration, false)
mock.ExpectQuery(selectAnyFromTokens).
WithArgs(mockCNSIGUID, mockUserGUID).
WillReturnRows(expectedCNSITokenRecordRow)
Expand Down
11 changes: 6 additions & 5 deletions components/app-core/backend/repository/cnsis/pgsql_cnsis.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import (
var listCNSIs = `SELECT guid, name, cnsi_type, api_endpoint, auth_endpoint, token_endpoint, doppler_logging_endpoint, skip_ssl_validation
FROM cnsis`

var listCNSIsByUser = `SELECT c.guid, c.name, c.cnsi_type, c.api_endpoint, t.user_guid, t.token_expiry, c.skip_ssl_validation
var listCNSIsByUser = `SELECT c.guid, c.name, c.cnsi_type, c.api_endpoint, t.user_guid, t.token_expiry, c.skip_ssl_validation, t.disconnected
FROM cnsis c, tokens t
WHERE c.guid = t.cnsi_guid AND t.token_type=$1 AND t.user_guid=$2`
WHERE c.guid = t.cnsi_guid AND t.token_type=$1 AND t.user_guid=$2 AND t.disconnected = '0'`

var findCNSI = `SELECT guid, name, cnsi_type, api_endpoint, auth_endpoint, token_endpoint, doppler_logging_endpoint, skip_ssl_validation
FROM cnsis
Expand Down Expand Up @@ -112,12 +112,13 @@ func (p *PostgresCNSIRepository) ListByUser(userGUID string) ([]*RegisteredClust

for rows.Next() {
var (
pCNSIType string
pURL string
pCNSIType string
pURL string
disconnected bool
)

cluster := new(RegisteredCluster)
err := rows.Scan(&cluster.GUID, &cluster.Name, &pCNSIType, &pURL, &cluster.Account, &cluster.TokenExpiry, &cluster.SkipSSLValidation)
err := rows.Scan(&cluster.GUID, &cluster.Name, &pCNSIType, &pURL, &cluster.Account, &cluster.TokenExpiry, &cluster.SkipSSLValidation, &disconnected)
if err != nil {
return nil, fmt.Errorf("Unable to scan cluster records: %v", err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type PortalProxy interface {
GetCNSIRecord(guid string) (CNSIRecord, error)
GetCNSIRecordByEndpoint(endpoint string) (CNSIRecord, error)
GetCNSITokenRecord(cnsiGUID string, userGUID string) (TokenRecord, bool)
GetCNSITokenRecordWithDisconnected(cnsiGUID string, userGUID string) (TokenRecord, bool)
GetCNSIUser(cnsiGUID string, userGUID string) (*ConnectedUser, bool)
GetConfig() *PortalConfig

Expand Down
2 changes: 2 additions & 0 deletions components/app-core/backend/repository/interfaces/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type TokenRecord struct {
AuthToken string
RefreshToken string
TokenExpiry int64
Disconnected bool
}

type CFInfo struct {
Expand Down Expand Up @@ -95,6 +96,7 @@ type PortalConfig struct {
EncryptionKeyVolume string `configName:"ENCRYPTION_KEY_VOLUME"`
EncryptionKeyFilename string `configName:"ENCRYPTION_KEY_FILENAME"`
EncryptionKey string `configName:"ENCRYPTION_KEY"`
AutoRegisterCFUrl string `configName:"AUTO_REG_CF_URL"`
CFAdminIdentifier string
CloudFoundryInfo *CFInfo
HTTPS bool
Expand Down
Loading

0 comments on commit bef83de

Please sign in to comment.