Skip to content

Commit

Permalink
sql: populate pg_catalog.pg_default_acl table
Browse files Browse the repository at this point in the history
Release note (sql change): Populate pg_catalog.pg_default_acl.
This is important for tracking which default privileges are defined
in the database.

pg_catalog.pg_default_acl has 5 columns.
oid oid - row identifier
defaclrole oid - oid of the role the default privileges are defined for
defaclnamespace oid - oid of the schema the default privileges are defined in
defaclobjtype char - r = relation (table, view), S = sequence, f = function, T = type, n = schema
defaclacl aclitem[] - string representation of default privileges, following the format
    "$1=$2/$3" where $1 is the grantee's username, $2 is a list of characters representing
    the privileges and $3 is the grantor (which is currently always an empty string in CRDB).

Privileges are represented by chars in the aclitem[] representation.
CREATE = 'C'
SELECT = 'r'
INSERT = 'a'
DELETE = 'd'
UPDATE = 'w'
USAGE = 'U'
CONNECT = 'c'

See: https://www.postgresql.org/docs/current/ddl-priv.html#PRIVILEGES-SUMMARY-TABLE
for the table of Postgres supported privileges and their char representations.

See: https://www.postgresql.org/docs/13/catalog-pg-default-acl.html for postgres' definition
of pg_catalog.pg_default_acl.
  • Loading branch information
RichardJCai committed Jul 22, 2021
1 parent b2e8805 commit 0644bef
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 2 deletions.
104 changes: 104 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/pg_catalog_pg_default_acl
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
statement ok
ALTER DEFAULT PRIVILEGES GRANT SELECT ON TABLES TO PUBLIC;
ALTER DEFAULT PRIVILEGES GRANT USAGE ON TYPES TO PUBLIC;
ALTER DEFAULT PRIVILEGES GRANT USAGE ON SCHEMAS TO PUBLIC;
ALTER DEFAULT PRIVILEGES GRANT SELECT ON SEQUENCES TO PUBLIC;

# Public should appear as an empty string with privileges.
query OOOTT colnames,rowsort
SELECT * FROM PG_CATALOG.PG_DEFAULT_ACL
----
oid defaclrole defaclnamespace defaclobjtype defaclacl
4149409857 1546506610 0 T {=U/}
4149409857 1546506610 0 n {=U/}
4149409857 1546506610 0 r {=r/}
4149409857 1546506610 0 S {=r/}

statement ok
CREATE USER foo

statement ok
CREATE USER bar

statement ok
ALTER DEFAULT PRIVILEGES GRANT ALL ON TABLES TO foo, bar;
ALTER DEFAULT PRIVILEGES GRANT ALL ON TYPES TO foo, bar;
ALTER DEFAULT PRIVILEGES GRANT ALL ON SCHEMAS TO foo, bar;
ALTER DEFAULT PRIVILEGES GRANT ALL ON SEQUENCES TO foo, bar;

query OOOTT colnames,rowsort
SELECT * FROM PG_CATALOG.PG_DEFAULT_ACL
----
oid defaclrole defaclnamespace defaclobjtype defaclacl
4149409857 1546506610 0 T {bar=U/,foo=U/,=U/}
4149409857 1546506610 0 n {bar=CU/,foo=CU/,=U/}
4149409857 1546506610 0 r {bar=Cadrw/,foo=Cadrw/,=r/}
4149409857 1546506610 0 S {bar=Cadrw/,foo=Cadrw/,=r/}

statement ok
GRANT foo, bar TO root;

statement ok
ALTER DEFAULT PRIVILEGES FOR ROLE foo, bar GRANT ALL ON TABLES TO foo, bar;
ALTER DEFAULT PRIVILEGES FOR ROLE foo, bar GRANT ALL ON TYPES TO foo, bar;
ALTER DEFAULT PRIVILEGES FOR ROLE foo, bar GRANT ALL ON SCHEMAS TO foo, bar;
ALTER DEFAULT PRIVILEGES FOR ROLE foo, bar GRANT ALL ON SEQUENCES TO foo, bar;

# 12 rows should exist, 4 for each role, root, foo and bar.
query OOOTT colnames,rowsort
SELECT * FROM PG_CATALOG.PG_DEFAULT_ACL
----
oid defaclrole defaclnamespace defaclobjtype defaclacl
542080048 1791217281 0 n {bar=CU/,foo=CU/}
542080048 1791217281 0 r {bar=Cadrw/,foo=Cadrw/}
542080048 1791217281 0 S {bar=Cadrw/,foo=Cadrw/}
542080048 1791217281 0 T {bar=U/,foo=U/}
38059971 2026795574 0 r {bar=Cadrw/,foo=Cadrw/}
38059971 2026795574 0 S {bar=Cadrw/,foo=Cadrw/}
38059971 2026795574 0 T {bar=U/,foo=U/}
38059971 2026795574 0 n {bar=CU/,foo=CU/}
4149409857 1546506610 0 n {bar=CU/,foo=CU/,=U/}
4149409857 1546506610 0 r {bar=Cadrw/,foo=Cadrw/,=r/}
4149409857 1546506610 0 S {bar=Cadrw/,foo=Cadrw/,=r/}
4149409857 1546506610 0 T {bar=U/,foo=U/,=U/}

statement ok
ALTER DEFAULT PRIVILEGES FOR ROLE foo, bar REVOKE ALL ON TABLES FROM foo, bar;
ALTER DEFAULT PRIVILEGES FOR ROLE foo, bar REVOKE ALL ON TYPES FROM foo, bar;
ALTER DEFAULT PRIVILEGES FOR ROLE foo, bar REVOKE ALL ON SCHEMAS FROM foo, bar;
ALTER DEFAULT PRIVILEGES FOR ROLE foo, bar REVOKE ALL ON SEQUENCES FROM foo, bar;

# Revoking all should remove 8 rows, 4 for each foo and bar.
query OOOTT colnames,rowsort
SELECT * FROM PG_CATALOG.PG_DEFAULT_ACL
----
oid defaclrole defaclnamespace defaclobjtype defaclacl
4149409857 1546506610 0 T {bar=U/,foo=U/,=U/}
4149409857 1546506610 0 n {bar=CU/,foo=CU/,=U/}
4149409857 1546506610 0 r {bar=Cadrw/,foo=Cadrw/,=r/}
4149409857 1546506610 0 S {bar=Cadrw/,foo=Cadrw/,=r/}

statement ok
ALTER DEFAULT PRIVILEGES REVOKE SELECT ON TABLES FROM foo, bar, public;
ALTER DEFAULT PRIVILEGES REVOKE ALL ON TYPES FROM foo, bar, public;
ALTER DEFAULT PRIVILEGES REVOKE ALL ON SCHEMAS FROM foo, bar, public;
ALTER DEFAULT PRIVILEGES REVOKE ALL ON SEQUENCES FROM foo, bar, public;

# Revoke ALL from types, schemas, sequences and select from tables.
# Only one entry should be left, for tables and 'r' should not be present.
query OOOTT colnames,rowsort
SELECT * FROM PG_CATALOG.PG_DEFAULT_ACL
----
oid defaclrole defaclnamespace defaclobjtype defaclacl
4149409857 1546506610 0 r {bar=Cadw/,foo=Cadw/}

# GRANT, DROP and ZONECONFIG should not show up in defaclacl.
statement ok
ALTER DEFAULT PRIVILEGES REVOKE ALL ON TABLES FROM foo, bar, public;
ALTER DEFAULT PRIVILEGES GRANT GRANT, DROP, ZONECONFIG ON TABLES TO foo;

query OOOTT colnames,rowsort
SELECT * FROM PG_CATALOG.PG_DEFAULT_ACL
----
oid defaclrole defaclnamespace defaclobjtype defaclacl
4149409857 1546506610 0 r {foo=/}
91 changes: 90 additions & 1 deletion pkg/sql/pg_catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/sql/parser"
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
"github.com/cockroachdb/cockroach/pkg/sql/privilege"
"github.com/cockroachdb/cockroach/pkg/sql/sem/builtins"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/sql/sqlerrors"
Expand Down Expand Up @@ -1107,9 +1108,84 @@ var pgCatalogDefaultACLTable = virtualSchemaTable{
https://www.postgresql.org/docs/9.6/catalog-pg-default-acl.html`,
schema: vtable.PGCatalogDefaultACL,
populate: func(ctx context.Context, p *planner, dbContext catalog.DatabaseDescriptor, addRow func(...tree.Datum) error) error {
h := makeOidHasher()
if dbContext.GetDefaultPrivileges() == nil {
return nil
}
for _, defaultPrivs := range dbContext.GetDefaultPrivileges().DefaultPrivileges {
// Need to consider the case of USAGE for Public as well.
for objectType, privs := range defaultPrivs.DefaultPrivilegesPerObject {
// Type of object this entry is for:
// r = relation (table, view), S = sequence, f = function, T = type, n = schema.
var c string
switch objectType {
case tree.Tables:
c = "r"
case tree.Sequences:
c = "S"
case tree.Types:
c = "T"
case tree.Schemas:
c = "n"
}
privilegeObjectType := targetObjectToPrivilegeObject[objectType]
arr := tree.NewDArray(types.String)
for _, userPrivs := range privs.Users {
var user string
if userPrivs.UserProto.Decode().IsPublicRole() {
// Postgres represents Public in defacl as an empty string.
user = ""
} else {
user = userPrivs.UserProto.Decode().Normalized()
}

privileges := privilege.ListFromBitField(
userPrivs.Privileges, privilegeObjectType,
)
defaclItem := fmt.Sprintf(`%s=%s/%s`,
user,
privileges.ListToACL(
privilegeObjectType,
),
// TODO(richardjcai): CockroachDB currently does not track grantors
// See: https://github.com/cockroachdb/cockroach/issues/67442.
"", /* grantor */
)

if len(defaclItem) != 0 {
if err := arr.Append(
tree.NewDString(defaclItem)); err != nil {
return err
}
}
}

if len(arr.Array) == 0 {
continue
}

// TODO(richardjcai): Update this logic once default privileges on
// schemas are supported.
// See: https://github.com/cockroachdb/cockroach/issues/67376.
schemaName := ""
rowOid := h.DBSchemaRoleOid(
dbContext.GetID(),
schemaName,
defaultPrivs.UserProto.Decode().Normalized(),
)
if err := addRow(
rowOid, // row identifier oid
h.UserOid(defaultPrivs.UserProto.Decode()), // defaclrole oid
oidZero, // defaclnamespace oid
tree.NewDString(c), // defaclobjtype char
arr, // defaclacl aclitem[]
); err != nil {
return err
}
}
}
return nil
},
unimplemented: true,
}

var (
Expand Down Expand Up @@ -3389,6 +3465,7 @@ const (
operatorTypeTag
enumEntryTypeTag
rewriteTypeTag
dbSchemaRoleTypeTag
)

func (h oidHasher) writeTypeTag(tag oidTypeTag) {
Expand Down Expand Up @@ -3559,6 +3636,18 @@ func (h oidHasher) rewriteOid(source descpb.ID, depended descpb.ID) *tree.DOid {
return h.getOid()
}

// DBSchemaRoleOid creates an OID based on the combination of a db/schema/role.
// This is used to generate a unique row identifier for pg_default_acl.
func (h oidHasher) DBSchemaRoleOid(
dbID descpb.ID, scName string, normalizedRole string,
) *tree.DOid {
h.writeTypeTag(dbSchemaRoleTypeTag)
h.writeDB(dbID)
h.writeSchema(scName)
h.writeStr(normalizedRole)
return h.getOid()
}

func tableOid(id descpb.ID) *tree.DOid {
return tree.NewDOid(tree.DInt(id))
}
Expand Down
35 changes: 34 additions & 1 deletion pkg/sql/privilege/privilege.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func (pl List) Less(i, j int) bool {
}

// names returns a list of privilege names in the same
// order as 'pl'.
// order as "pl".
func (pl List) names() []string {
ret := make([]string, len(pl))
for i, p := range pl {
Expand Down Expand Up @@ -236,3 +236,36 @@ func GetValidPrivilegesForObject(objectType ObjectType) List {
panic(errors.AssertionFailedf("unknown object type %s", objectType))
}
}

// ListToACL converts a list of privileges to a list of Postgres
// ACL items.
// See: https://www.postgresql.org/docs/13/ddl-priv.html#PRIVILEGE-ABBREVS-TABLE
// for privileges and their ACL abbreviations.
func (pl List) ListToACL(objectType ObjectType) string {
privileges := pl
// If ALL is present, explode ALL into the underlying privileges.
if pl.Contains(ALL) {
privileges = GetValidPrivilegesForObject(objectType)
}
chars := make([]string, len(privileges))
for _, privilege := range privileges {
switch privilege {
case CREATE:
chars = append(chars, "C")
case SELECT:
chars = append(chars, "r")
case INSERT:
chars = append(chars, "a")
case DELETE:
chars = append(chars, "d")
case UPDATE:
chars = append(chars, "w")
case USAGE:
chars = append(chars, "U")
case CONNECT:
chars = append(chars, "c")
}
}
sort.Strings(chars)
return strings.Join(chars, "")
}

0 comments on commit 0644bef

Please sign in to comment.