fix(oracle): 保留堡垒机用户名的原始认证语义 (#3113)
Co-authored-by: zipg <4047349+zipg@users.noreply.github.com>
This commit is contained in:
parent
9903747381
commit
58168472a8
|
|
@ -620,7 +620,7 @@ func buildDSN(params connectParams) string {
|
|||
if strings.HasPrefix(strings.ToLower(connectionString), "oracle://") {
|
||||
return connectionString
|
||||
}
|
||||
username := oracleAuthUsername(params.Username)
|
||||
username := params.Username
|
||||
options := parseURLParams(params.URLParams)
|
||||
if params.SysDBA {
|
||||
options["AUTH TYPE"] = "SYSDBA"
|
||||
|
|
@ -653,42 +653,6 @@ func buildDSN(params connectParams) string {
|
|||
return buildGoOraURL(params.Host, port, service, username, params.Password, options)
|
||||
}
|
||||
|
||||
func oracleAuthUsername(username string) string {
|
||||
if username == "" || isQuotedOracleUsername(username) || !oracleUsernameRequiresQuoting(username) {
|
||||
return username
|
||||
}
|
||||
// Oracle logon accepts quoted identifiers for users that cannot be
|
||||
// represented as regular identifiers, such as bastion usernames with ':'.
|
||||
return `"` + strings.ReplaceAll(username, `"`, `""`) + `"`
|
||||
}
|
||||
|
||||
func isQuotedOracleUsername(username string) bool {
|
||||
return len(username) >= 2 && strings.HasPrefix(username, `"`) && strings.HasSuffix(username, `"`)
|
||||
}
|
||||
|
||||
func oracleUsernameRequiresQuoting(username string) bool {
|
||||
for index, ch := range username {
|
||||
if index == 0 {
|
||||
if !isAsciiLetter(ch) {
|
||||
return true
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !isAsciiLetter(ch) && !isAsciiDigit(ch) && ch != '_' && ch != '$' && ch != '#' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isAsciiLetter(ch rune) bool {
|
||||
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')
|
||||
}
|
||||
|
||||
func isAsciiDigit(ch rune) bool {
|
||||
return ch >= '0' && ch <= '9'
|
||||
}
|
||||
|
||||
func buildGoOraJDBC(user, password, connStr string, options map[string]string) string {
|
||||
if options == nil {
|
||||
options = make(map[string]string)
|
||||
|
|
@ -699,7 +663,8 @@ func buildGoOraJDBC(user, password, connStr string, options map[string]string) s
|
|||
|
||||
func buildGoOraURL(server string, port int, service, user, password string, options map[string]string) string {
|
||||
// go-ora v2.9.0 uses path escaping for user/password, leaving ':' unescaped.
|
||||
// Userinfo escaping keeps usernames such as "9008888:reader" intact.
|
||||
// Userinfo escaping keeps bastion usernames such as 9008888:reader intact
|
||||
// without changing their authentication semantics.
|
||||
ret := fmt.Sprintf(
|
||||
"oracle://%s@%s/%s",
|
||||
url.UserPassword(user, password).String(),
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ func TestBuildDSNUsesConnectionStringWhenProvided(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestBuildDSNEncodesColonInCredentials(t *testing.T) {
|
||||
func TestBuildDSNPreservesBastionUsernameAndEncodesCredentials(t *testing.T) {
|
||||
dsn := buildDSN(connectParams{
|
||||
Host: "db.example.com",
|
||||
Port: 1521,
|
||||
|
|
@ -313,11 +313,11 @@ func TestBuildDSNEncodesColonInCredentials(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
password, _ := parsed.User.Password()
|
||||
if parsed.User.Username() != `"9008888:reader"` || password != "dbx:pass" {
|
||||
if parsed.User.Username() != "9008888:reader" || password != "dbx:pass" {
|
||||
t.Fatalf("credentials should survive URL parsing, dsn=%s username=%q password=%q", dsn, parsed.User.Username(), password)
|
||||
}
|
||||
if !strings.HasPrefix(parsed.User.String(), "%229008888%3Areader%22:") {
|
||||
t.Fatalf("Oracle auth username should be quoted and escaped for non-regular identifiers, dsn=%s", dsn)
|
||||
if !strings.HasPrefix(parsed.User.String(), "9008888%3Areader:") {
|
||||
t.Fatalf("bastion username should be escaped without being quoted, dsn=%s", dsn)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -333,7 +333,7 @@ func TestBuildDSNEncodesColonInCredentialsFromJDBCServiceURL(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
password, _ := parsed.User.Password()
|
||||
if parsed.User.Username() != `"9008888:reader"` || password != "dbx:pass" {
|
||||
if parsed.User.Username() != "9008888:reader" || password != "dbx:pass" {
|
||||
t.Fatalf("credentials should survive JDBC URL conversion, dsn=%s username=%q password=%q", dsn, parsed.User.Username(), password)
|
||||
}
|
||||
if parsed.Host != "db.example.com:1521" || strings.TrimPrefix(parsed.Path, "/") != "XE" {
|
||||
|
|
@ -341,21 +341,21 @@ func TestBuildDSNEncodesColonInCredentialsFromJDBCServiceURL(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestOracleAuthUsernameQuotesOnlyNonRegularIdentifiers(t *testing.T) {
|
||||
tests := map[string]string{
|
||||
"scott": "scott",
|
||||
"test": "test",
|
||||
"SCOTT_1": "SCOTT_1",
|
||||
"9008888:reader": `"9008888:reader"`,
|
||||
"abc:def": `"abc:def"`,
|
||||
`"abc:def"`: `"abc:def"`,
|
||||
`abc"def`: `"abc""def"`,
|
||||
}
|
||||
func TestBuildDSNPreservesExplicitlyQuotedUsername(t *testing.T) {
|
||||
dsn := buildDSN(connectParams{
|
||||
Host: "db.example.com",
|
||||
Port: 1521,
|
||||
Database: "XE",
|
||||
Username: `"abc:def"`,
|
||||
Password: "dbx:pass",
|
||||
})
|
||||
|
||||
for input, want := range tests {
|
||||
if got := oracleAuthUsername(input); got != want {
|
||||
t.Fatalf("oracleAuthUsername(%q) = %q, want %q", input, got, want)
|
||||
}
|
||||
parsed, err := url.Parse(dsn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if parsed.User.Username() != `"abc:def"` {
|
||||
t.Fatalf("explicitly quoted username should remain unchanged, dsn=%s username=%q", dsn, parsed.User.Username())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue