Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ require (
github.com/antchfx/xpath v1.3.3
github.com/cockroachdb/apd/v3 v3.2.3
github.com/cockroachdb/errors v1.7.5
github.com/dolthub/dolt/go v0.40.5-0.20260929214903-5a7f241b2a54
github.com/dolthub/dolt/go v0.40.5-0.20260930093343-863fefb78c63
github.com/dolthub/eventsapi_schema v0.0.0-20260715220557-d9b4a1c6b4d4
github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2
github.com/dolthub/go-mysql-server v0.20.1-0.20260929213643-0c0ebff14743
github.com/dolthub/go-mysql-server v0.20.1-0.20260930085504-71ba14497def
github.com/dolthub/pg_query_go/v6 v6.0.0-20260922094746-ae7577e21dfe
github.com/dolthub/sqllogictest/go v0.0.0-20260624223518-788480b24166
github.com/dolthub/vitess v0.0.0-20260916192104-15c5c4158b37
github.com/dolthub/vitess v0.0.0-20260930074555-77cf155d2465
github.com/fatih/color v1.13.0
github.com/go-sql-driver/mysql v1.9.3
github.com/goccy/go-json v0.10.2
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,8 @@ github.com/dolthub/aws-sdk-go-ini-parser v0.0.0-20250305001723-2821c37f6c12 h1:I
github.com/dolthub/aws-sdk-go-ini-parser v0.0.0-20250305001723-2821c37f6c12/go.mod h1:rN7X8BHwkjPcfMQQ2QTAq/xM3leUSGLfb+1Js7Y6TVo=
github.com/dolthub/dolt-mcp v0.3.4 h1:AyG5cw+fNWXDHXujtQnqUPZrpWtPg6FN6yYtjv1pP44=
github.com/dolthub/dolt-mcp v0.3.4/go.mod h1:bCZ7KHvDYs+M0e+ySgmGiNvLhcwsN7bbf5YCyillLrk=
github.com/dolthub/dolt/go v0.40.5-0.20260929214903-5a7f241b2a54 h1:7CSe8xzJkUFzOfCEaDBwIyhZSHc3x5RHFFEPnduXT2I=
github.com/dolthub/dolt/go v0.40.5-0.20260929214903-5a7f241b2a54/go.mod h1:wfLKZ2n/LHLqZHM8NFkHAZxo77A+SBNC4Hb2sbz9Neo=
github.com/dolthub/dolt/go v0.40.5-0.20260930093343-863fefb78c63 h1:WzC64UZKfXI0NkdTK9K8Uv/SA7CxcUj8Ry1WiIq+CgE=
github.com/dolthub/dolt/go v0.40.5-0.20260930093343-863fefb78c63/go.mod h1:L+OuMUB/S45GM2NLmK40slCKtX20FLG8Euwlrv4kmjI=
github.com/dolthub/eventsapi_schema v0.0.0-20260715220557-d9b4a1c6b4d4 h1:0mg9QEFdkkBwJMxvz1tCjHYmfG2iIC6aShj1InDq9/M=
github.com/dolthub/eventsapi_schema v0.0.0-20260715220557-d9b4a1c6b4d4/go.mod h1:SSLraQS/jGLYFgff3vuZ+JbVUct6vyEeMzjLBqWqoyM=
github.com/dolthub/file-locks v0.1.1 h1:8oKr20PflAVHkroB5Kq1BrtzesvKop9onekWEse1Qr8=
Expand All @@ -260,8 +260,8 @@ github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 h1:u3PMzfF8RkKd3lB9pZ2bfn0qEG+1G
github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2/go.mod h1:mIEZOHnFx4ZMQeawhw9rhsj+0zwQj7adVsnBX7t+eKY=
github.com/dolthub/go-icu-regex v0.0.0-20260610153742-72563bc7ca83 h1:FEMjCGEroDnY/BXyAffVZxUpXhP2GpoUJyyq5KaLn8c=
github.com/dolthub/go-icu-regex v0.0.0-20260610153742-72563bc7ca83/go.mod h1:F3cnm+vMRK1HaU6+rNqQrOCyR03HHhR1GWG2gnPOqaE=
github.com/dolthub/go-mysql-server v0.20.1-0.20260929213643-0c0ebff14743 h1:4iyHwLCKpommapKwAEhr59r/JeoC6TBIGxNI8c9rr78=
github.com/dolthub/go-mysql-server v0.20.1-0.20260929213643-0c0ebff14743/go.mod h1:Qnp0PJNtR8JPjkmRDMShYh7Fy9jiSYLFgfQCe6Gmnxg=
github.com/dolthub/go-mysql-server v0.20.1-0.20260930085504-71ba14497def h1:RaEFafMdBdCe4VmnBW2tdD1P6J9QfxQBPVh8CQxAzBU=
github.com/dolthub/go-mysql-server v0.20.1-0.20260930085504-71ba14497def/go.mod h1:HJmzcei8r2MpvfOZ+slXBJUGNNf3D8xKpkWH7yKCrX8=
github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63 h1:OAsXLAPL4du6tfbBgK0xXHZkOlos63RdKYS3Sgw/dfI=
github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63/go.mod h1:lV7lUeuDhH5thVGDCKXbatwKy2KW80L4rMT46n+Y2/Q=
github.com/dolthub/ishell v0.0.0-20260414231531-5f031e3e9037 h1:oIW9HwuWrhxv+4HZxA+QQSKHLqWFyXZ2FmNjUYwkdiM=
Expand All @@ -272,8 +272,8 @@ github.com/dolthub/pg_query_go/v6 v6.0.0-20260922094746-ae7577e21dfe h1:5QQHZM+Z
github.com/dolthub/pg_query_go/v6 v6.0.0-20260922094746-ae7577e21dfe/go.mod h1:qnrZP3/1slFl2Bq5yw38HLOsArZareGwdpEceriblLc=
github.com/dolthub/sqllogictest/go v0.0.0-20260624223518-788480b24166 h1:fdexJBpLDyAGnFbviZYfZ0jhLMnbTWTh39CyQ1EhA28=
github.com/dolthub/sqllogictest/go v0.0.0-20260624223518-788480b24166/go.mod h1:e/FIZVvT2IR53HBCAo41NjqgtEnjMJGKca3Y/dAmZaA=
github.com/dolthub/vitess v0.0.0-20260916192104-15c5c4158b37 h1:F8kNW/lTJQ70FMP/s9MtQpGjURvbwE/B318Q6uEFG30=
github.com/dolthub/vitess v0.0.0-20260916192104-15c5c4158b37/go.mod h1:5SVEJgAhw5nnQUFnGgKI1Svqes1Mw+zKUsalyxmOuG0=
github.com/dolthub/vitess v0.0.0-20260930074555-77cf155d2465 h1:inhFizBUDk1No+zUSijSgqIOuDCTfKIwzQZn1+7eIDE=
github.com/dolthub/vitess v0.0.0-20260930074555-77cf155d2465/go.mod h1:5SVEJgAhw5nnQUFnGgKI1Svqes1Mw+zKUsalyxmOuG0=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
Expand Down
79 changes: 70 additions & 9 deletions server/auth/auth_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/cockroachdb/errors"
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/plan"
vitess "github.com/dolthub/vitess/go/vt/sqlparser"

"github.com/dolthub/doltgresql/core"
Expand Down Expand Up @@ -63,6 +64,7 @@ type AuthorizationHandler struct {
}

var _ sql.AuthorizationHandler = (*AuthorizationHandler)(nil)
var _ sql.ResolvedTableAuthorizationHandler = (*AuthorizationHandler)(nil)

// NewQueryState implements the sql.AuthorizationHandler interface.
func (h *AuthorizationHandler) NewQueryState(ctx *sql.Context) sql.AuthorizationQueryState {
Expand Down Expand Up @@ -167,7 +169,7 @@ func (h *AuthorizationHandler) HandleAuth(ctx *sql.Context, aqs sql.Authorizatio
}
for _, privilege := range privileges {
if !HasDatabasePrivilege(roleDatabaseKey, privilege) && !HasDatabasePrivilege(publicDatabaseKey, privilege) {
return errors.Errorf("permission denied for database %s", database)
return pgerror.Newf(pgcode.InsufficientPrivilege, "permission denied for database %s", database)
}
}
}
Expand All @@ -188,10 +190,14 @@ func (h *AuthorizationHandler) HandleAuth(ctx *sql.Context, aqs sql.Authorizatio
return err
}
}
case AuthTargetType_TableIdentifiers:
case AuthTargetType_TableIdentifiers, AuthTargetType_ViewIdentifiers:
if len(auth.TargetNames)%3 != 0 {
return errors.Errorf("table identifiers has an unsupported count: %d", len(auth.TargetNames))
}
relationKind := "table"
if auth.TargetType == AuthTargetType_ViewIdentifiers {
relationKind = "view"
}
for i := 0; i < len(auth.TargetNames); i += 3 {
// TODO: handle database
schemaName, err := core.GetSchemaName(ctx, nil, auth.TargetNames[i+1])
Expand All @@ -200,7 +206,7 @@ func (h *AuthorizationHandler) HandleAuth(ctx *sql.Context, aqs sql.Authorizatio
// This will error later in the process, so we'll pass auth for now.
return nil
}
err = checkPrivilegeOnTable(state, schemaName, auth.TargetNames[i+2], privileges)
err = checkPrivilegeOnTable(state, relationKind, schemaName, auth.TargetNames[i+2], privileges)
if err != nil {
return err
}
Expand Down Expand Up @@ -304,6 +310,26 @@ func (h *AuthorizationHandler) CheckTable(ctx *sql.Context, aqs sql.Authorizatio
return nil
}

// HandleResolvedTableAuth implements the sql.ResolvedTableAuthorizationHandler interface.
Comment thread
Hydrocharged marked this conversation as resolved.
Comment thread
Hydrocharged marked this conversation as resolved.
func (h *AuthorizationHandler) HandleResolvedTableAuth(ctx *sql.Context, aqs sql.AuthorizationQueryState, auth vitess.AuthInformation, node sql.Node) error {
if auth.TargetType == AuthTargetType_TableIdentifiers {
switch node := node.(type) {
case *plan.ResolvedTable:
schemaName := auth.TargetNames[1]
if schemaTable, ok := node.UnderlyingTable().(sql.DatabaseSchemaTable); ok {
schemaName = schemaTable.DatabaseSchema().SchemaName()
} else if node.Database().Name() == sql.InformationSchemaDatabaseName {
schemaName = sql.InformationSchemaDatabaseName
}
auth.TargetNames = []string{auth.TargetNames[0], schemaName, auth.TargetNames[2]}
case *plan.SubqueryAlias:
// This case will always resolve to a view
auth.TargetType = AuthTargetType_ViewIdentifiers
}
}
return h.HandleAuth(ctx, aqs, auth)
}

// dbName uses the current database from the context if a database is not specified, otherwise it returns the given
// database name.
func (h *AuthorizationHandler) dbName(ctx *sql.Context, dbName string) string {
Expand All @@ -327,14 +353,14 @@ func checkPrivilegeOnSchema(state AuthorizationQueryState, schemaName string, pr
}
for _, privilege := range privileges {
if !HasSchemaPrivilege(roleSchemaKey, privilege) && !HasSchemaPrivilege(publicSchemaKey, privilege) {
return errors.Errorf("permission denied for schema %s", schemaName)
return pgerror.Newf(pgcode.InsufficientPrivilege, "permission denied for schema %s", schemaName)
}
}
return nil
}

// checkPrivilegeOnTable checks privileges for given table provided with schema name.
func checkPrivilegeOnTable(state AuthorizationQueryState, schemaName, tableName string, privileges []Privilege) error {
// checkPrivilegeOnTable checks privileges for given table or view provided with schema name.
func checkPrivilegeOnTable(state AuthorizationQueryState, relationKind, schemaName, tableName string, privileges []Privilege) error {
roleTableKey := TablePrivilegeKey{
Role: state.role.ID(),
Table: doltdb.TableName{Name: tableName, Schema: schemaName},
Expand All @@ -345,12 +371,47 @@ func checkPrivilegeOnTable(state AuthorizationQueryState, schemaName, tableName
}
for _, privilege := range privileges {
if !HasTablePrivilege(roleTableKey, privilege) && !HasTablePrivilege(publicTableKey, privilege) {
return errors.Errorf("permission denied for table %s", tableName)
if privilege == Privilege_SELECT && isPublicCatalogTable(schemaName, tableName) {
//TODO: catalogs are granted to PUBLIC by default, so deny them once REVOKE ... FROM PUBLIC is supported
continue
}
return pgerror.Newf(pgcode.InsufficientPrivilege, "permission denied for %s %s", relationKind, tableName)
}
}
return nil
}

// restrictedCatalogTables are the pg_catalog relations that Postgres does not grant SELECT on to PUBLIC.
var restrictedCatalogTables = map[string]struct{}{
"pg_authid": {},
"pg_backend_memory_contexts": {},
"pg_config": {},
"pg_file_settings": {},
"pg_hba_file_rules": {},
"pg_ident_file_mappings": {},
"pg_largeobject": {},
"pg_replication_origin_status": {},
"pg_shadow": {},
"pg_shmem_allocations": {},
"pg_statistic": {},
"pg_statistic_ext_data": {},
"pg_subscription": {},
"pg_user_mapping": {},
}

// isPublicCatalogTable returns whether the given relation is a system catalog that every role may read.
func isPublicCatalogTable(schemaName string, tableName string) bool {
switch schemaName {
case "pg_catalog":
_, restricted := restrictedCatalogTables[tableName]
return !restricted
case "information_schema":
return true
default:
return false
}
}

// checkPrivilegeOnSequence checks privileges for given sequence provided with schema name.
func checkPrivilegeOnSequence(state AuthorizationQueryState, schemaName, seqName string, privileges []Privilege) error {
roleSequenceKey := SequencePrivilegeKey{
Expand All @@ -365,7 +426,7 @@ func checkPrivilegeOnSequence(state AuthorizationQueryState, schemaName, seqName
}
for _, privilege := range privileges {
if !HasSequencePrivilege(roleSequenceKey, privilege) && !HasSequencePrivilege(publicSequenceKey, privilege) {
return errors.Errorf("permission denied for sequence %s", seqName)
return pgerror.Newf(pgcode.InsufficientPrivilege, "permission denied for sequence %s", seqName)
}
}
return nil
Expand Down Expand Up @@ -402,7 +463,7 @@ func checkPrivilegeOnRoutine(ctx *sql.Context, state AuthorizationQueryState, sc
//TODO: built-in routines are granted to PUBLIC by default, so deny them once REVOKE ... FROM PUBLIC is supported
return nil
}
return errors.Errorf("permission denied for routine %s", routineName)
return pgerror.Newf(pgcode.InsufficientPrivilege, "permission denied for routine %s", routineName)
}
}
return nil
Expand Down
1 change: 1 addition & 0 deletions server/auth/auth_information.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const (
AuthTargetType_DatabaseIdentifiers = "DB_IDENTS"
AuthTargetType_SchemaIdentifiers = "DB_SCH_IDENTS"
AuthTargetType_TableIdentifiers = "DB_SCH_TABLE_IDENTS"
AuthTargetType_ViewIdentifiers = "DB_SCH_VIEW_IDENTS"
AuthTargetType_FunctionIdentifiers = "DB_SCH_FUNCTION_IDENTS"
AuthTargetType_SequenceIdentifiers = "DB_SCH_SEQUENCE_IDENTS"
AuthTargetType_TODO = "TODO"
Expand Down
Loading
Loading