Skip to content

Commit

Permalink
release 0.111.0
Browse files Browse the repository at this point in the history
  • Loading branch information
stfnmllr committed Nov 22, 2022
1 parent 1b6d495 commit 58f002a
Show file tree
Hide file tree
Showing 21 changed files with 515 additions and 205 deletions.
2 changes: 1 addition & 1 deletion .reuse/dep5
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ Disclaimer: The code in this project may include calls to APIs (“API Calls”)
parties the right to use of access any SAP External Product, through API Calls.

Files: *
Copyright: 2014-2022 SAP SE
Copyright: 2014-2022 SAP SE or an SAP affiliate company and go-hdb contributors
License: Apache-2.0
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ all:
@echo execute tests on latest go version
go test ./...
@echo execute tests on older supported go versions
go1.18.7 test ./...
go1.18.8 test ./...
@echo "reuse (license) check"
reuse lint

Expand All @@ -26,8 +26,8 @@ tools:

#install additional go versions
go:
go install golang.org/dl/go1.18.7@latest
go1.18.7 download
go install golang.org/dl/go1.18.8@latest
go1.18.8 download

#install fsfe reuse tool (https://git.fsfe.org/reuse/tool)
# pre-conditions:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ go test --tags unit
* Native Go implementation (no C libraries, CGO).
* Go <http://golang.org/pkg/database/sql> package compliant.
* Support of database/sql/driver Execer and Queryer interface for parameter free statements and queries.
* Support of 'bulk' and 'many' inserts.
* Support of 'bulk' query execution.
* Support of UTF-8 to / from CESU-8 encodings for HANA Unicode types.
* Built-in support of HANA decimals as Go rational numbers <http://golang.org/pkg/math/big>.
* Support of Large Object streaming.
Expand Down
10 changes: 9 additions & 1 deletion RELEASENOTES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
Release Notes
=============

## Release 0.111

#### Release Notes

- X509 authentication: check validity period of client certificates before connecting to database (incompatible change)

added additional error return parameter to NewX509AuthConnector because of early X509 certificate and key validation

## Release 0.110

Release 0.110.1 (upgrade urgency: no need for upgrade)
Expand All @@ -20,7 +28,7 @@ Release 0.110.1 (upgrade urgency: no need for upgrade)

As to the restrictions and redundancy comming with some of the options the first three are going to be set to deprecated
and the latter two (extended arguments and function argument) are kept going forward. Until go-hdb release V1.0 would
be available the deprecated options can be used further by switching on 'legacy mode' on connector level.
be available the deprecated options can be used further by switching on connector 'legacy mode'.

## Release 0.109

Expand Down
144 changes: 97 additions & 47 deletions driver/authattrs.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
package driver

import (
"bytes"
"strings"
"sync"

p "github.com/SAP/go-hdb/driver/internal/protocol"
"github.com/SAP/go-hdb/driver/internal/protocol/x509"
)

// authAttrs is holding authentication relevant attributes.
type authAttrs struct {
hasCookie atomicBool
mu sync.RWMutex
_username, _password string // basic authentication
_clientCert, _clientKey []byte // X509
_token string // JWT
_logonname string // session cookie login does need logon name provided by JWT authentication.
_sessionCookie []byte // authentication via session cookie (HDB currently does support only SAML and JWT - go-hdb JWT)
_refreshPassword func() (password string, ok bool)
_refreshClientCert func() (clientCert, clientKey []byte, ok bool)
_refreshToken func() (token string, ok bool)
hasCookie atomicBool
mu sync.RWMutex
_username, _password string // basic authentication
_certKey *x509.CertKey // X509
_token string // JWT
_logonname string // session cookie login does need logon name provided by JWT authentication.
_sessionCookie []byte // authentication via session cookie (HDB currently does support only SAML and JWT - go-hdb JWT)
_refreshPassword func() (password string, ok bool)
_refreshClientCert func() (clientCert, clientKey []byte, ok bool)
_refreshToken func() (token string, ok bool)
}

/*
keep c as the instance name, so that the generated help does have the same variable name when object is
included in connector
keep c as the instance name, so that the generated help does have
the same instance variable name when included in connector
*/

func isJWTToken(token string) bool { return strings.HasPrefix(token, "ey") }
Expand All @@ -37,8 +37,8 @@ func (c *authAttrs) cookieAuth() *p.Auth {
c.mu.RLock()
defer c.mu.RUnlock()

auth := p.NewAuth(c._logonname) // important: for session cookie auth we do need the logonname from JWT auth.
auth.AddSessionCookie(c._sessionCookie, c._logonname, clientID) // And for HANA onPrem the final session cookie req needs the logonname as well.
auth := p.NewAuth(c._logonname) // important: for session cookie auth we do need the logonname from JWT auth,
auth.AddSessionCookie(c._sessionCookie, c._logonname, clientID) // and for HANA onPrem the final session cookie req needs the logonname as well.
return auth
}

Expand All @@ -47,8 +47,8 @@ func (c *authAttrs) auth() *p.Auth {
defer c.mu.RUnlock()

auth := p.NewAuth(c._username) // use username as logonname
if c._clientCert != nil && c._clientKey != nil {
auth.AddX509(c._clientCert, c._clientKey)
if c._certKey != nil {
auth.AddX509(c._certKey)
}
if c._token != "" {
auth.AddJWT(c._token)
Expand All @@ -63,42 +63,86 @@ func (c *authAttrs) auth() *p.Auth {
return auth
}

func (c *authAttrs) refresh(auth *p.Auth) bool {
switch method := auth.Method().(type) {
func (c *authAttrs) refreshPassword(passwordSetter p.AuthPasswordSetter) (bool, error) {
password := c.Password() // save password
c.mu.RLock() // read lock as callback function might call connector read functions
if password != c._password { // if password has changed it got refreshed in the meanwhile by another connection
c.mu.RUnlock()
return true, nil
}
if c._refreshPassword != nil {
if password, ok := c._refreshPassword(); ok && c._password != password {
c.mu.RUnlock()
c.mu.Lock()
c._password = password
passwordSetter.SetPassword(password)
c.mu.Unlock()
return true, nil
}
}
c.mu.RUnlock()
return false, nil
}

case p.AuthPasswordSetter:
if fn := c._refreshPassword; fn != nil {
if password, ok := fn(); ok && c._password != password {
c.mu.Lock()
c._password = password
c.mu.Unlock()
method.SetPassword(password)
return true
}
func (c *authAttrs) refreshToken(tokenSetter p.AuthTokenSetter) (bool, error) {
token := c.Token() // save token
c.mu.RLock() // read lock as callback function might call connector read functions
if token != c._token { // if token has changed it got refreshed in the meanwhile by another connection
c.mu.RUnlock()
return true, nil
}
if c._refreshToken != nil {
if token, ok := c._refreshToken(); ok && c._token != token {
c.mu.RUnlock()
c.mu.Lock()
c._token = token
tokenSetter.SetToken(token)
c.mu.Unlock()
return true, nil
}
case p.AuthTokenSetter:
if fn := c._refreshToken; fn != nil {
if token, ok := fn(); ok && c._token != token {
c.mu.Lock()
c._token = token
}
c.mu.RUnlock()
return false, nil
}

func (c *authAttrs) refreshCertKey(certKeySetter p.AuthCertKeySetter) (bool, error) {
certKey := c._certKey // save certKey
c.mu.RLock() // read lock as callback function might call connector read functions
if certKey != c._certKey { // if certKey has changed it got refreshed in the meanwhile by another connection
c.mu.RUnlock()
return true, nil
}
if c._refreshClientCert != nil {
if clientCert, clientKey, ok := c._refreshClientCert(); ok && !c._certKey.Equal(clientCert, clientKey) {
c.mu.RUnlock()
c.mu.Lock()
certKey, err := x509.NewCertKey(clientCert, clientKey)
if err != nil {
c.mu.Unlock()
method.SetToken(token)
return true
return false, err
}
c._certKey = certKey
certKeySetter.SetCertKey(certKey)
c.mu.Unlock()
return true, nil
}
}
c.mu.RUnlock()
return false, nil
}

func (c *authAttrs) refresh(auth *p.Auth) (bool, error) {
switch method := auth.Method().(type) {

case p.AuthPasswordSetter:
return c.refreshPassword(method)
case p.AuthTokenSetter:
return c.refreshToken(method)
case p.AuthCertKeySetter:
if fn := c._refreshClientCert; fn != nil {
if clientCert, clientKey, ok := fn(); ok && !(bytes.Equal(c._clientCert, clientCert) && bytes.Equal(c._clientKey, clientKey)) {
c.mu.Lock()
c._clientCert = clientCert
c._clientKey = clientKey
c.mu.Unlock()
method.SetCertKey(clientCert, clientKey)
return true
}
}
return c.refreshCertKey(method)
default:
return false, nil
}
return false
}

func (c *authAttrs) invalidateCookie() { c.hasCookie.Store(false) }
Expand Down Expand Up @@ -132,6 +176,8 @@ func (c *authAttrs) RefreshPassword() func() (password string, ok bool) {
}

// SetRefreshPassword sets the callback function for basic authentication password refresh.
// The callback function might be called simultaneously from multiple goroutines if registered
// for more than one Connector.
func (c *authAttrs) SetRefreshPassword(refreshPassword func() (password string, ok bool)) {
c.mu.Lock()
defer c.mu.Unlock()
Expand All @@ -142,7 +188,7 @@ func (c *authAttrs) SetRefreshPassword(refreshPassword func() (password string,
func (c *authAttrs) ClientCert() (clientCert, clientKey []byte) {
c.mu.RLock()
defer c.mu.RUnlock()
return c._clientCert, c._clientKey
return c._certKey.Cert(), c._certKey.Key()
}

// RefreshClientCert returns the callback function for X509 authentication client certificate and key refresh.
Expand All @@ -153,6 +199,8 @@ func (c *authAttrs) RefreshClientCert() func() (clientCert, clientKey []byte, ok
}

// SetRefreshClientCert sets the callback function for X509 authentication client certificate and key refresh.
// The callback function might be called simultaneously from multiple goroutines if registered
// for more than one Connector.
func (c *authAttrs) SetRefreshClientCert(refreshClientCert func() (clientCert, clientKey []byte, ok bool)) {
c.mu.Lock()
defer c.mu.Unlock()
Expand All @@ -170,6 +218,8 @@ func (c *authAttrs) RefreshToken() func() (token string, ok bool) {
}

// SetRefreshToken sets the callback function for JWT authentication token refresh.
// The callback function might be called simultaneously from multiple goroutines if registered
// for more than one Connector.
func (c *authAttrs) SetRefreshToken(refreshToken func() (token string, ok bool)) {
c.mu.Lock()
defer c.mu.Unlock()
Expand Down

0 comments on commit 58f002a

Please sign in to comment.