From b21f6b796f75fc6d131d7f14310f9ad9143427dc Mon Sep 17 00:00:00 2001 From: candiduslynx Date: Thu, 16 May 2024 19:04:20 +0300 Subject: [PATCH 1/7] fix: `SHOW USERS` output with insufficient privileges --- .gitignore | 3 ++ pkg/sdk/users.go | 82 +++++++++++++++++++++++++++++++----------------- 2 files changed, 57 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index 68c9efca6f..fb90812d95 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ dist/ # Test environment variables test.env + +# deps +vendor/ \ No newline at end of file diff --git a/pkg/sdk/users.go b/pkg/sdk/users.go index 3a4f74b80b..2b08f73fe1 100644 --- a/pkg/sdk/users.go +++ b/pkg/sdk/users.go @@ -62,7 +62,7 @@ type User struct { type userDBRow struct { Name string `db:"name"` CreatedOn time.Time `db:"created_on"` - LoginName string `db:"login_name"` + LoginName sql.NullString `db:"login_name"` DisplayName sql.NullString `db:"display_name"` FirstName sql.NullString `db:"first_name"` LastName sql.NullString `db:"last_name"` @@ -70,41 +70,31 @@ type userDBRow struct { MinsToUnlock sql.NullString `db:"mins_to_unlock"` DaysToExpiry sql.NullString `db:"days_to_expiry"` Comment sql.NullString `db:"comment"` - Disabled bool `db:"disabled"` - MustChangePassword bool `db:"must_change_password"` - SnowflakeLock bool `db:"snowflake_lock"` + Disabled sql.NullBool `db:"disabled"` + MustChangePassword sql.NullBool `db:"must_change_password"` + SnowflakeLock sql.NullBool `db:"snowflake_lock"` DefaultWarehouse sql.NullString `db:"default_warehouse"` - DefaultNamespace string `db:"default_namespace"` - DefaultRole string `db:"default_role"` - DefaultSecondaryRoles string `db:"default_secondary_roles"` - ExtAuthnDuo bool `db:"ext_authn_duo"` - ExtAuthnUid string `db:"ext_authn_uid"` - MinsToBypassMfa string `db:"mins_to_bypass_mfa"` - Owner string `db:"owner"` + DefaultNamespace sql.NullString `db:"default_namespace"` + DefaultRole sql.NullString `db:"default_role"` + DefaultSecondaryRoles sql.NullString `db:"default_secondary_roles"` + ExtAuthnDuo sql.NullBool `db:"ext_authn_duo"` + ExtAuthnUid sql.NullString `db:"ext_authn_uid"` + MinsToBypassMfa sql.NullString `db:"mins_to_bypass_mfa"` + Owner sql.NullString `db:"owner"` LastSuccessLogin sql.NullTime `db:"last_success_login"` ExpiresAtTime sql.NullTime `db:"expires_at_time"` LockedUntilTime sql.NullTime `db:"locked_until_time"` - HasPassword bool `db:"has_password"` - HasRsaPublicKey bool `db:"has_rsa_public_key"` + HasPassword sql.NullBool `db:"has_password"` + HasRsaPublicKey sql.NullBool `db:"has_rsa_public_key"` } func (row userDBRow) convert() *User { user := &User{ - Name: row.Name, - CreatedOn: row.CreatedOn, - LoginName: row.LoginName, - Disabled: row.Disabled, - MustChangePassword: row.MustChangePassword, - SnowflakeLock: row.SnowflakeLock, - DefaultNamespace: row.DefaultNamespace, - DefaultRole: row.DefaultRole, - DefaultSecondaryRoles: row.DefaultSecondaryRoles, - ExtAuthnDuo: row.ExtAuthnDuo, - ExtAuthnUid: row.ExtAuthnUid, - MinsToBypassMfa: row.MinsToBypassMfa, - Owner: row.Owner, - HasPassword: row.HasPassword, - HasRsaPublicKey: row.HasRsaPublicKey, + Name: row.Name, + CreatedOn: row.CreatedOn, + } + if row.LoginName.Valid { + user.LoginName = row.LoginName.String } if row.DisplayName.Valid { user.DisplayName = row.DisplayName.String @@ -127,9 +117,39 @@ func (row userDBRow) convert() *User { if row.Comment.Valid { user.Comment = row.Comment.String } + if row.Disabled.Valid { + user.Disabled = row.Disabled.Bool + } + if row.MustChangePassword.Valid { + user.MustChangePassword = row.MustChangePassword.Bool + } + if row.SnowflakeLock.Valid { + user.SnowflakeLock = row.SnowflakeLock.Bool + } if row.DefaultWarehouse.Valid { user.DefaultWarehouse = row.DefaultWarehouse.String } + if row.DefaultNamespace.Valid { + user.DefaultNamespace = row.DefaultNamespace.String + } + if row.DefaultRole.Valid { + user.DefaultRole = row.DefaultRole.String + } + if row.DefaultSecondaryRoles.Valid { + user.DefaultSecondaryRoles = row.DefaultSecondaryRoles.String + } + if row.ExtAuthnDuo.Valid { + user.ExtAuthnDuo = row.ExtAuthnDuo.Bool + } + if row.ExtAuthnUid.Valid { + user.ExtAuthnUid = row.ExtAuthnUid.String + } + if row.MinsToBypassMfa.Valid { + user.MinsToBypassMfa = row.MinsToBypassMfa.String + } + if row.Owner.Valid { + user.Owner = row.Owner.String + } if row.LastSuccessLogin.Valid { user.LastSuccessLogin = row.LastSuccessLogin.Time } @@ -139,6 +159,12 @@ func (row userDBRow) convert() *User { if row.LockedUntilTime.Valid { user.LockedUntilTime = row.LockedUntilTime.Time } + if row.HasPassword.Valid { + user.HasPassword = row.HasPassword.Bool + } + if row.HasRsaPublicKey.Valid { + user.HasRsaPublicKey = row.HasRsaPublicKey.Bool + } return user } From 8863a9c34ed01f1e790d0feda1ffa400b342754a Mon Sep 17 00:00:00 2001 From: candiduslynx Date: Sun, 19 May 2024 14:57:20 +0300 Subject: [PATCH 2/7] add `snowflakesql` pkg --- pkg/sdk/snowflakesql/bool.go | 57 ++++++++++++++++++++++++++++++++++++ pkg/sdk/users.go | 54 ++++++++++++++++++---------------- 2 files changed, 85 insertions(+), 26 deletions(-) create mode 100644 pkg/sdk/snowflakesql/bool.go diff --git a/pkg/sdk/snowflakesql/bool.go b/pkg/sdk/snowflakesql/bool.go new file mode 100644 index 0000000000..d8fa74f9b3 --- /dev/null +++ b/pkg/sdk/snowflakesql/bool.go @@ -0,0 +1,57 @@ +package snowflakesql + +import "database/sql/driver" + +// NullBool is inspired by sql.NullBool, but it will handle `"null"` passed as value, too +type NullBool struct { + Bool bool + Valid bool // Valid is true if Bool is not NULL +} + +// Scan implements the [Scanner] interface. +func (n *NullBool) Scan(value any) error { + switch value := value.(type) { + case nil: // untyped nil + n.Bool, n.Valid = false, false + return nil + case string: + return n.fromString(value) + case *string: + if value == nil { + n.Bool, n.Valid = false, false + return nil + } + return n.fromString(*value) + } + + return n.convertAny(value) +} + +func (n *NullBool) fromString(value string) error { + if value == "null" { + // Sadly, we have to do this, as Snowflake can return `"null"` for boolean fields. + // E.g., `disabled` field in `SHOW USERS` output. + n.Bool, n.Valid = false, false + return nil + } + return n.convertAny(value) +} + +func (n *NullBool) convertAny(value any) error { + res, err := driver.Bool.ConvertValue(value) + if err != nil { + n.Bool, n.Valid = false, false + return err + } + + n.Bool, n.Valid = res.(bool) + return nil +} + +// Value implements the [driver.Valuer] interface. +func (n NullBool) Value() (driver.Value, error) { + if !n.Valid { + return nil, nil + } + return n.Bool, nil +} diff --git a/pkg/sdk/users.go b/pkg/sdk/users.go index 2b08f73fe1..2d63adeec6 100644 --- a/pkg/sdk/users.go +++ b/pkg/sdk/users.go @@ -6,6 +6,8 @@ import ( "errors" "fmt" "time" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/snowflakesql" ) var ( @@ -60,32 +62,32 @@ type User struct { HasRsaPublicKey bool } type userDBRow struct { - Name string `db:"name"` - CreatedOn time.Time `db:"created_on"` - LoginName sql.NullString `db:"login_name"` - DisplayName sql.NullString `db:"display_name"` - FirstName sql.NullString `db:"first_name"` - LastName sql.NullString `db:"last_name"` - Email sql.NullString `db:"email"` - MinsToUnlock sql.NullString `db:"mins_to_unlock"` - DaysToExpiry sql.NullString `db:"days_to_expiry"` - Comment sql.NullString `db:"comment"` - Disabled sql.NullBool `db:"disabled"` - MustChangePassword sql.NullBool `db:"must_change_password"` - SnowflakeLock sql.NullBool `db:"snowflake_lock"` - DefaultWarehouse sql.NullString `db:"default_warehouse"` - DefaultNamespace sql.NullString `db:"default_namespace"` - DefaultRole sql.NullString `db:"default_role"` - DefaultSecondaryRoles sql.NullString `db:"default_secondary_roles"` - ExtAuthnDuo sql.NullBool `db:"ext_authn_duo"` - ExtAuthnUid sql.NullString `db:"ext_authn_uid"` - MinsToBypassMfa sql.NullString `db:"mins_to_bypass_mfa"` - Owner sql.NullString `db:"owner"` - LastSuccessLogin sql.NullTime `db:"last_success_login"` - ExpiresAtTime sql.NullTime `db:"expires_at_time"` - LockedUntilTime sql.NullTime `db:"locked_until_time"` - HasPassword sql.NullBool `db:"has_password"` - HasRsaPublicKey sql.NullBool `db:"has_rsa_public_key"` + Name string `db:"name"` + CreatedOn time.Time `db:"created_on"` + LoginName sql.NullString `db:"login_name"` + DisplayName sql.NullString `db:"display_name"` + FirstName sql.NullString `db:"first_name"` + LastName sql.NullString `db:"last_name"` + Email sql.NullString `db:"email"` + MinsToUnlock sql.NullString `db:"mins_to_unlock"` + DaysToExpiry sql.NullString `db:"days_to_expiry"` + Comment sql.NullString `db:"comment"` + Disabled snowflakesql.NullBool `db:"disabled"` + MustChangePassword snowflakesql.NullBool `db:"must_change_password"` + SnowflakeLock snowflakesql.NullBool `db:"snowflake_lock"` + DefaultWarehouse sql.NullString `db:"default_warehouse"` + DefaultNamespace sql.NullString `db:"default_namespace"` + DefaultRole sql.NullString `db:"default_role"` + DefaultSecondaryRoles sql.NullString `db:"default_secondary_roles"` + ExtAuthnDuo snowflakesql.NullBool `db:"ext_authn_duo"` + ExtAuthnUid sql.NullString `db:"ext_authn_uid"` + MinsToBypassMfa sql.NullString `db:"mins_to_bypass_mfa"` + Owner sql.NullString `db:"owner"` + LastSuccessLogin sql.NullTime `db:"last_success_login"` + ExpiresAtTime sql.NullTime `db:"expires_at_time"` + LockedUntilTime sql.NullTime `db:"locked_until_time"` + HasPassword snowflakesql.NullBool `db:"has_password"` + HasRsaPublicKey snowflakesql.NullBool `db:"has_rsa_public_key"` } func (row userDBRow) convert() *User { From 14ac532dfdb5e07d6298e340a363bab4629da521 Mon Sep 17 00:00:00 2001 From: candiduslynx Date: Sun, 19 May 2024 15:00:48 +0300 Subject: [PATCH 3/7] less diff --- pkg/sdk/users.go | 46 ++++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/pkg/sdk/users.go b/pkg/sdk/users.go index 2d63adeec6..773758967f 100644 --- a/pkg/sdk/users.go +++ b/pkg/sdk/users.go @@ -64,7 +64,7 @@ type User struct { type userDBRow struct { Name string `db:"name"` CreatedOn time.Time `db:"created_on"` - LoginName sql.NullString `db:"login_name"` + LoginName string `db:"login_name"` DisplayName sql.NullString `db:"display_name"` FirstName sql.NullString `db:"first_name"` LastName sql.NullString `db:"last_name"` @@ -76,13 +76,13 @@ type userDBRow struct { MustChangePassword snowflakesql.NullBool `db:"must_change_password"` SnowflakeLock snowflakesql.NullBool `db:"snowflake_lock"` DefaultWarehouse sql.NullString `db:"default_warehouse"` - DefaultNamespace sql.NullString `db:"default_namespace"` - DefaultRole sql.NullString `db:"default_role"` - DefaultSecondaryRoles sql.NullString `db:"default_secondary_roles"` + DefaultNamespace string `db:"default_namespace"` + DefaultRole string `db:"default_role"` + DefaultSecondaryRoles string `db:"default_secondary_roles"` ExtAuthnDuo snowflakesql.NullBool `db:"ext_authn_duo"` - ExtAuthnUid sql.NullString `db:"ext_authn_uid"` - MinsToBypassMfa sql.NullString `db:"mins_to_bypass_mfa"` - Owner sql.NullString `db:"owner"` + ExtAuthnUid string `db:"ext_authn_uid"` + MinsToBypassMfa string `db:"mins_to_bypass_mfa"` + Owner string `db:"owner"` LastSuccessLogin sql.NullTime `db:"last_success_login"` ExpiresAtTime sql.NullTime `db:"expires_at_time"` LockedUntilTime sql.NullTime `db:"locked_until_time"` @@ -92,11 +92,15 @@ type userDBRow struct { func (row userDBRow) convert() *User { user := &User{ - Name: row.Name, - CreatedOn: row.CreatedOn, - } - if row.LoginName.Valid { - user.LoginName = row.LoginName.String + Name: row.Name, + CreatedOn: row.CreatedOn, + LoginName: row.LoginName, + DefaultNamespace: row.DefaultNamespace, + DefaultRole: row.DefaultRole, + DefaultSecondaryRoles: row.DefaultSecondaryRoles, + ExtAuthnUid: row.ExtAuthnUid, + MinsToBypassMfa: row.MinsToBypassMfa, + Owner: row.Owner, } if row.DisplayName.Valid { user.DisplayName = row.DisplayName.String @@ -131,27 +135,9 @@ func (row userDBRow) convert() *User { if row.DefaultWarehouse.Valid { user.DefaultWarehouse = row.DefaultWarehouse.String } - if row.DefaultNamespace.Valid { - user.DefaultNamespace = row.DefaultNamespace.String - } - if row.DefaultRole.Valid { - user.DefaultRole = row.DefaultRole.String - } - if row.DefaultSecondaryRoles.Valid { - user.DefaultSecondaryRoles = row.DefaultSecondaryRoles.String - } if row.ExtAuthnDuo.Valid { user.ExtAuthnDuo = row.ExtAuthnDuo.Bool } - if row.ExtAuthnUid.Valid { - user.ExtAuthnUid = row.ExtAuthnUid.String - } - if row.MinsToBypassMfa.Valid { - user.MinsToBypassMfa = row.MinsToBypassMfa.String - } - if row.Owner.Valid { - user.Owner = row.Owner.String - } if row.LastSuccessLogin.Valid { user.LastSuccessLogin = row.LastSuccessLogin.Time } From 3b457df4a22d7911b0e58a77d86b45a7d59e1913 Mon Sep 17 00:00:00 2001 From: candiduslynx Date: Mon, 20 May 2024 11:30:47 +0300 Subject: [PATCH 4/7] add tests --- pkg/sdk/snowflakesql/bool.go | 70 ++++++++++++++++------ pkg/sdk/snowflakesql/bool_test.go | 97 +++++++++++++++++++++++++++++++ pkg/sdk/users.go | 52 ++++++++--------- 3 files changed, 176 insertions(+), 43 deletions(-) create mode 100644 pkg/sdk/snowflakesql/bool_test.go diff --git a/pkg/sdk/snowflakesql/bool.go b/pkg/sdk/snowflakesql/bool.go index d8fa74f9b3..c58cbb284c 100644 --- a/pkg/sdk/snowflakesql/bool.go +++ b/pkg/sdk/snowflakesql/bool.go @@ -1,44 +1,80 @@ package snowflakesql -import "database/sql/driver" +import ( + "database/sql/driver" + "fmt" + "reflect" +) -// NullBool is inspired by sql.NullBool, but it will handle `"null"` passed as value, too -type NullBool struct { +// Bool is inspired by sql.NullBool, but it will handle `"null"` passed as value, too +type Bool struct { Bool bool Valid bool // Valid is true if Bool is not NULL } // Scan implements the [Scanner] interface. -func (n *NullBool) Scan(value any) error { +func (n *Bool) Scan(value any) error { switch value := value.(type) { case nil: // untyped nil n.Bool, n.Valid = false, false return nil + case bool: + return n.fromBool(&value) + case *bool: + return n.fromBool(value) case string: - return n.fromString(value) + return n.fromString(&value) case *string: - if value == nil { - n.Bool, n.Valid = false, false - return nil - } - return n.fromString(*value) + return n.fromString(value) + default: + return n.convertAny(value) } +} - return n.convertAny(value) +func (n *Bool) fromBool(value *bool) error { + if n.Valid = value != nil; n.Valid { + n.Bool = *value + } else { + n.Bool = false + } + return nil } -func (n *NullBool) fromString(value string) error { - if value == "null" { +func (n *Bool) fromString(value *string) error { + if value == nil { + n.Bool, n.Valid = false, false + return nil + } + + str := *value + if str == "null" { // Sadly, we have to do this, as Snowflake can return `"null"` for boolean fields. // E.g., `disabled` field in `SHOW USERS` output. n.Bool, n.Valid = false, false return nil } - return n.convertAny(value) + + return n.convertAny(str) } -func (n *NullBool) convertAny(value any) error { - res, err := driver.Bool.ConvertValue(value) +func (n *Bool) convertAny(value any) error { + v := reflect.ValueOf(value) + for v.Kind() == reflect.Pointer { + if v.IsNil() { + // nil pointer to some value + n.Bool, n.Valid = false, false + return nil + } + v = v.Elem() + } + + if !v.CanInterface() { + // shouldn't be here, but fail without panic + n.Bool, n.Valid = false, false + return fmt.Errorf("can't convert %v (%T) into bool", value, value) + } + + res, err := driver.Bool.ConvertValue(v.Interface()) if err != nil { n.Bool, n.Valid = false, false return err @@ -49,7 +85,7 @@ func (n *NullBool) convertAny(value any) error { } // Value implements the [driver.Valuer] interface. -func (n NullBool) Value() (driver.Value, error) { +func (n Bool) Value() (driver.Value, error) { if !n.Valid { return nil, nil } diff --git a/pkg/sdk/snowflakesql/bool_test.go b/pkg/sdk/snowflakesql/bool_test.go new file mode 100644 index 0000000000..c444845641 --- /dev/null +++ b/pkg/sdk/snowflakesql/bool_test.go @@ -0,0 +1,97 @@ +package snowflakesql + +import ( + "errors" + "fmt" + "io" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBool_Scan(t *testing.T) { + type testCase struct { + from any + expected Bool + err error + } + + for _, tc := range []testCase{ + { + // passing nil will result in invalid Bool without errors + from: nil, + expected: Bool{}, + err: nil, + }, + { + from: "1", + expected: Bool{Valid: true, Bool: true}, + }, + { + from: &[]string{"1"}[0], // pointer to string + expected: Bool{Valid: true, Bool: true}, + }, + { + from: "2", + expected: Bool{}, + err: errors.New("sql/driver: couldn't convert \"2\" into type bool"), + }, + { + from: &[]string{"2"}[0], // pointer to string + expected: Bool{}, + err: errors.New("sql/driver: couldn't convert \"2\" into type bool"), + }, + { + from: "", + expected: Bool{}, + err: errors.New("sql/driver: couldn't convert \"\" into type bool"), + }, + { + from: &[]string{""}[0], // pointer to string + expected: Bool{}, + err: errors.New("sql/driver: couldn't convert \"\" into type bool"), + }, + { + from: true, + expected: Bool{Valid: true, Bool: true}, + }, + { + from: &[]bool{true}[0], // pointer to bool + expected: Bool{Valid: true, Bool: true}, + }, + { + from: false, + expected: Bool{Valid: true, Bool: false}, + }, + { + from: &[]bool{false}[0], // pointer to bool + expected: Bool{Valid: true, Bool: false}, + }, + { + from: int64(123), + expected: Bool{}, + err: errors.New("sql/driver: couldn't convert 123 into type bool"), + }, + { + from: &[]int64{123}[0], // pointer to int64 + expected: Bool{}, + err: errors.New("sql/driver: couldn't convert 123 into type bool"), + }, + { + from: io.Copy, + expected: Bool{}, + err: errors.New("(func(io.Writer, io.Reader) (int64, error)) into type bool"), + }, + } { + t.Run(fmt.Sprint(tc.from), func(t *testing.T) { + var res Bool + err := res.Scan(tc.from) + if tc.err == nil { + assert.NoError(t, err) + } else { + assert.ErrorContains(t, err, tc.err.Error()) + } + assert.Exactly(t, tc.expected, res) + }) + } +} diff --git a/pkg/sdk/users.go b/pkg/sdk/users.go index 773758967f..2e371063b1 100644 --- a/pkg/sdk/users.go +++ b/pkg/sdk/users.go @@ -62,32 +62,32 @@ type User struct { HasRsaPublicKey bool } type userDBRow struct { - Name string `db:"name"` - CreatedOn time.Time `db:"created_on"` - LoginName string `db:"login_name"` - DisplayName sql.NullString `db:"display_name"` - FirstName sql.NullString `db:"first_name"` - LastName sql.NullString `db:"last_name"` - Email sql.NullString `db:"email"` - MinsToUnlock sql.NullString `db:"mins_to_unlock"` - DaysToExpiry sql.NullString `db:"days_to_expiry"` - Comment sql.NullString `db:"comment"` - Disabled snowflakesql.NullBool `db:"disabled"` - MustChangePassword snowflakesql.NullBool `db:"must_change_password"` - SnowflakeLock snowflakesql.NullBool `db:"snowflake_lock"` - DefaultWarehouse sql.NullString `db:"default_warehouse"` - DefaultNamespace string `db:"default_namespace"` - DefaultRole string `db:"default_role"` - DefaultSecondaryRoles string `db:"default_secondary_roles"` - ExtAuthnDuo snowflakesql.NullBool `db:"ext_authn_duo"` - ExtAuthnUid string `db:"ext_authn_uid"` - MinsToBypassMfa string `db:"mins_to_bypass_mfa"` - Owner string `db:"owner"` - LastSuccessLogin sql.NullTime `db:"last_success_login"` - ExpiresAtTime sql.NullTime `db:"expires_at_time"` - LockedUntilTime sql.NullTime `db:"locked_until_time"` - HasPassword snowflakesql.NullBool `db:"has_password"` - HasRsaPublicKey snowflakesql.NullBool `db:"has_rsa_public_key"` + Name string `db:"name"` + CreatedOn time.Time `db:"created_on"` + LoginName string `db:"login_name"` + DisplayName sql.NullString `db:"display_name"` + FirstName sql.NullString `db:"first_name"` + LastName sql.NullString `db:"last_name"` + Email sql.NullString `db:"email"` + MinsToUnlock sql.NullString `db:"mins_to_unlock"` + DaysToExpiry sql.NullString `db:"days_to_expiry"` + Comment sql.NullString `db:"comment"` + Disabled snowflakesql.Bool `db:"disabled"` + MustChangePassword snowflakesql.Bool `db:"must_change_password"` + SnowflakeLock snowflakesql.Bool `db:"snowflake_lock"` + DefaultWarehouse sql.NullString `db:"default_warehouse"` + DefaultNamespace string `db:"default_namespace"` + DefaultRole string `db:"default_role"` + DefaultSecondaryRoles string `db:"default_secondary_roles"` + ExtAuthnDuo snowflakesql.Bool `db:"ext_authn_duo"` + ExtAuthnUid string `db:"ext_authn_uid"` + MinsToBypassMfa string `db:"mins_to_bypass_mfa"` + Owner string `db:"owner"` + LastSuccessLogin sql.NullTime `db:"last_success_login"` + ExpiresAtTime sql.NullTime `db:"expires_at_time"` + LockedUntilTime sql.NullTime `db:"locked_until_time"` + HasPassword snowflakesql.Bool `db:"has_password"` + HasRsaPublicKey snowflakesql.Bool `db:"has_rsa_public_key"` } func (row userDBRow) convert() *User { From 7120d2e0c4350102627ba34902ca0bdffcda17d3 Mon Sep 17 00:00:00 2001 From: candiduslynx Date: Mon, 20 May 2024 11:40:18 +0300 Subject: [PATCH 5/7] less diff with helper --- pkg/sdk/snowflakesql/bool.go | 5 +++++ pkg/sdk/users.go | 24 ++++++------------------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/pkg/sdk/snowflakesql/bool.go b/pkg/sdk/snowflakesql/bool.go index c58cbb284c..0a4eda633c 100644 --- a/pkg/sdk/snowflakesql/bool.go +++ b/pkg/sdk/snowflakesql/bool.go @@ -91,3 +91,8 @@ func (n Bool) Value() (driver.Value, error) { } return n.Bool, nil } + +// BoolValue returns either the default bool (false) if the Bool.Valid != true, of the underlying Bool.Value. +func (n Bool) BoolValue() bool { + return n.Valid && n.Bool +} diff --git a/pkg/sdk/users.go b/pkg/sdk/users.go index 2e371063b1..e037fa3534 100644 --- a/pkg/sdk/users.go +++ b/pkg/sdk/users.go @@ -95,12 +95,18 @@ func (row userDBRow) convert() *User { Name: row.Name, CreatedOn: row.CreatedOn, LoginName: row.LoginName, + Disabled: row.Disabled.BoolValue(), + MustChangePassword: row.MustChangePassword.BoolValue(), + SnowflakeLock: row.SnowflakeLock.BoolValue(), DefaultNamespace: row.DefaultNamespace, DefaultRole: row.DefaultRole, DefaultSecondaryRoles: row.DefaultSecondaryRoles, + ExtAuthnDuo: row.ExtAuthnDuo.BoolValue(), ExtAuthnUid: row.ExtAuthnUid, MinsToBypassMfa: row.MinsToBypassMfa, Owner: row.Owner, + HasPassword: row.HasPassword.BoolValue(), + HasRsaPublicKey: row.HasRsaPublicKey.BoolValue(), } if row.DisplayName.Valid { user.DisplayName = row.DisplayName.String @@ -123,21 +129,9 @@ func (row userDBRow) convert() *User { if row.Comment.Valid { user.Comment = row.Comment.String } - if row.Disabled.Valid { - user.Disabled = row.Disabled.Bool - } - if row.MustChangePassword.Valid { - user.MustChangePassword = row.MustChangePassword.Bool - } - if row.SnowflakeLock.Valid { - user.SnowflakeLock = row.SnowflakeLock.Bool - } if row.DefaultWarehouse.Valid { user.DefaultWarehouse = row.DefaultWarehouse.String } - if row.ExtAuthnDuo.Valid { - user.ExtAuthnDuo = row.ExtAuthnDuo.Bool - } if row.LastSuccessLogin.Valid { user.LastSuccessLogin = row.LastSuccessLogin.Time } @@ -147,12 +141,6 @@ func (row userDBRow) convert() *User { if row.LockedUntilTime.Valid { user.LockedUntilTime = row.LockedUntilTime.Time } - if row.HasPassword.Valid { - user.HasPassword = row.HasPassword.Bool - } - if row.HasRsaPublicKey.Valid { - user.HasRsaPublicKey = row.HasRsaPublicKey.Bool - } return user } From 526e9f6711f67c996d180a4e348efe15a83128fd Mon Sep 17 00:00:00 2001 From: candiduslynx Date: Mon, 20 May 2024 14:10:46 +0300 Subject: [PATCH 6/7] add integration --- pkg/sdk/testint/users_integration_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/sdk/testint/users_integration_test.go b/pkg/sdk/testint/users_integration_test.go index dcebb58d8c..817127ccc7 100644 --- a/pkg/sdk/testint/users_integration_test.go +++ b/pkg/sdk/testint/users_integration_test.go @@ -12,6 +12,7 @@ import ( func TestInt_UsersShow(t *testing.T) { client := testClient(t) + secondaryClient := testSecondaryClient(t) ctx := testContext(t) userTest, userCleanup := testClientHelper().User.CreateUserWithName(t, "USER_FOO") @@ -74,6 +75,14 @@ func TestInt_UsersShow(t *testing.T) { require.NoError(t, err) assert.Equal(t, 1, len(users)) }) + + t.Run("with like options", func(t *testing.T) { + users, err := secondaryClient.Users.Show(ctx, nil) + require.NoError(t, err) + assert.Contains(t, users, *userTest) + assert.Contains(t, users, *userTest2) + assert.Equal(t, 2, len(users)) + }) } func TestInt_UserCreate(t *testing.T) { From 7246bdd06b016ed5871af64ca085002c3fdc8b73 Mon Sep 17 00:00:00 2001 From: candiduslynx Date: Mon, 20 May 2024 15:26:12 +0300 Subject: [PATCH 7/7] compare by name only --- pkg/sdk/testint/users_integration_test.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/sdk/testint/users_integration_test.go b/pkg/sdk/testint/users_integration_test.go index 817127ccc7..7e346ad0d4 100644 --- a/pkg/sdk/testint/users_integration_test.go +++ b/pkg/sdk/testint/users_integration_test.go @@ -79,8 +79,14 @@ func TestInt_UsersShow(t *testing.T) { t.Run("with like options", func(t *testing.T) { users, err := secondaryClient.Users.Show(ctx, nil) require.NoError(t, err) - assert.Contains(t, users, *userTest) - assert.Contains(t, users, *userTest2) + found := 0 + // we can't compare via assert.Contains as not all the fields will be filled int + for _, u := range users { + if u.Name == userTest.Name || u.Name == userTest2.Name { + found++ + } + } + assert.Equal(t, 2, found) assert.Equal(t, 2, len(users)) }) }