Skip to content

Commit

Permalink
sql: with grant option/grant option for
Browse files Browse the repository at this point in the history
Release note (sql change): If the WITH GRANT OPTION flag is present when granting privileges to a user, then that user is able to grant those same privileges to subsequent users; otherwise, they cannot. If the GRANT OPTION FOR flag is present when revoking privileges from a user, then only the ability the grant those privileges is revoked from that user, not the privileges themselves
  • Loading branch information
jackcwu committed Nov 11, 2021
1 parent 4493953 commit 77e27f6
Show file tree
Hide file tree
Showing 24 changed files with 1,681 additions and 172 deletions.
2 changes: 1 addition & 1 deletion docs/generated/settings/settings-for-tenants.txt
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,4 @@ trace.debug.enable boolean false if set, traces for recent requests can be seen
trace.jaeger.agent string the address of a Jaeger agent to receive traces using the Jaeger UDP Thrift protocol, as <host>:<port>. If no port is specified, 6381 will be used.
trace.opentelemetry.collector string address of an OpenTelemetry trace collector to receive traces using the otel gRPC protocol, as <host>:<port>. If no port is specified, 4317 will be used.
trace.zipkin.collector string the address of a Zipkin instance to receive traces, as <host>:<port>. If no port is specified, 9411 will be used.
version version 21.2-4 set the active cluster version in the format '<major>.<minor>'
version version 21.2-6 set the active cluster version in the format '<major>.<minor>'
2 changes: 1 addition & 1 deletion docs/generated/settings/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,6 @@
<tr><td><code>trace.jaeger.agent</code></td><td>string</td><td><code></code></td><td>the address of a Jaeger agent to receive traces using the Jaeger UDP Thrift protocol, as <host>:<port>. If no port is specified, 6381 will be used.</td></tr>
<tr><td><code>trace.opentelemetry.collector</code></td><td>string</td><td><code></code></td><td>address of an OpenTelemetry trace collector to receive traces using the otel gRPC protocol, as <host>:<port>. If no port is specified, 4317 will be used.</td></tr>
<tr><td><code>trace.zipkin.collector</code></td><td>string</td><td><code></code></td><td>the address of a Zipkin instance to receive traces, as <host>:<port>. If no port is specified, 9411 will be used.</td></tr>
<tr><td><code>version</code></td><td>version</td><td><code>21.2-4</code></td><td>set the active cluster version in the format '<major>.<minor>'</td></tr>
<tr><td><code>version</code></td><td>version</td><td><code>21.2-6</code></td><td>set the active cluster version in the format '<major>.<minor>'</td></tr>
</tbody>
</table>
6 changes: 3 additions & 3 deletions docs/generated/sql/bnf/grant_stmt.bnf
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
grant_stmt ::=
'GRANT' 'ALL' 'PRIVILEGES' 'ON' targets 'TO' role_spec_list
| 'GRANT' 'ALL' 'ON' targets 'TO' role_spec_list
| 'GRANT' privilege_list 'ON' targets 'TO' role_spec_list
'GRANT' 'ALL' 'PRIVILEGES' 'ON' targets 'TO' role_spec_list opt_with_grant_option
| 'GRANT' 'ALL' 'ON' targets 'TO' role_spec_list opt_with_grant_option
| 'GRANT' privilege_list 'ON' targets 'TO' role_spec_list opt_with_grant_option
| 'GRANT' privilege_list 'TO' role_spec_list
| 'GRANT' privilege_list 'TO' role_spec_list 'WITH' 'ADMIN' 'OPTION'
3 changes: 3 additions & 0 deletions docs/generated/sql/bnf/revoke_stmt.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@ revoke_stmt ::=
'REVOKE' 'ALL' 'PRIVILEGES' 'ON' targets 'FROM' role_spec_list
| 'REVOKE' 'ALL' 'ON' targets 'FROM' role_spec_list
| 'REVOKE' privilege_list 'ON' targets 'FROM' role_spec_list
| 'REVOKE' 'GRANT' 'OPTION' 'FOR' 'ALL' 'PRIVILEGES' 'ON' targets 'FROM' role_spec_list
| 'REVOKE' 'GRANT' 'OPTION' 'FOR' 'ALL' 'ON' targets 'FROM' role_spec_list
| 'REVOKE' 'GRANT' 'OPTION' 'FOR' privilege_list 'ON' targets 'FROM' role_spec_list
| 'REVOKE' privilege_list 'FROM' role_spec_list
| 'REVOKE' 'ADMIN' 'OPTION' 'FOR' privilege_list 'FROM' role_spec_list
20 changes: 12 additions & 8 deletions docs/generated/sql/bnf/stmt_block.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -74,23 +74,27 @@ discard_stmt ::=
'DISCARD' 'ALL'

grant_stmt ::=
'GRANT' privileges 'ON' targets 'TO' role_spec_list
'GRANT' privileges 'ON' targets 'TO' role_spec_list opt_with_grant_option
| 'GRANT' privilege_list 'TO' role_spec_list
| 'GRANT' privilege_list 'TO' role_spec_list 'WITH' 'ADMIN' 'OPTION'
| 'GRANT' privileges 'ON' 'TYPE' target_types 'TO' role_spec_list
| 'GRANT' privileges 'ON' 'SCHEMA' schema_name_list 'TO' role_spec_list
| 'GRANT' privileges 'ON' 'ALL' 'TABLES' 'IN' 'SCHEMA' schema_name_list 'TO' role_spec_list
| 'GRANT' privileges 'ON' 'TYPE' target_types 'TO' role_spec_list opt_with_grant_option
| 'GRANT' privileges 'ON' 'SCHEMA' schema_name_list 'TO' role_spec_list opt_with_grant_option
| 'GRANT' privileges 'ON' 'ALL' 'TABLES' 'IN' 'SCHEMA' schema_name_list 'TO' role_spec_list opt_with_grant_option

prepare_stmt ::=
'PREPARE' table_alias_name prep_type_clause 'AS' preparable_stmt

revoke_stmt ::=
'REVOKE' privileges 'ON' targets 'FROM' role_spec_list
| 'REVOKE' 'GRANT' 'OPTION' 'FOR' privileges 'ON' targets 'FROM' role_spec_list
| 'REVOKE' privilege_list 'FROM' role_spec_list
| 'REVOKE' 'ADMIN' 'OPTION' 'FOR' privilege_list 'FROM' role_spec_list
| 'REVOKE' privileges 'ON' 'TYPE' target_types 'FROM' role_spec_list
| 'REVOKE' 'GRANT' 'OPTION' 'FOR' privileges 'ON' 'TYPE' target_types 'FROM' role_spec_list
| 'REVOKE' privileges 'ON' 'SCHEMA' schema_name_list 'FROM' role_spec_list
| 'REVOKE' 'GRANT' 'OPTION' 'FOR' privileges 'ON' 'SCHEMA' schema_name_list 'FROM' role_spec_list
| 'REVOKE' privileges 'ON' 'ALL' 'TABLES' 'IN' 'SCHEMA' schema_name_list 'FROM' role_spec_list
| 'REVOKE' 'GRANT' 'OPTION' 'FOR' privileges 'ON' 'ALL' 'TABLES' 'IN' 'SCHEMA' schema_name_list 'FROM' role_spec_list

savepoint_stmt ::=
'SAVEPOINT' name
Expand Down Expand Up @@ -321,6 +325,10 @@ targets ::=
role_spec_list ::=
( role_spec ) ( ( ',' role_spec ) )*

opt_with_grant_option ::=
'WITH' 'GRANT' 'OPTION'
|

privilege_list ::=
( privilege ) ( ( ',' privilege ) )*

Expand Down Expand Up @@ -2312,10 +2320,6 @@ alter_default_privileges_target_object ::=
| 'TYPES'
| 'SCHEMAS'

opt_with_grant_option ::=
'WITH' 'GRANT' 'OPTION'
|

role_option ::=
'CREATEROLE'
| 'NOCREATEROLE'
Expand Down
1 change: 1 addition & 0 deletions pkg/ccl/importccl/import_table_creation.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func MakeTestingSimpleTableDescriptor(
Privileges: descpb.NewPrivilegeDescriptor(
security.PublicRoleName(),
privilege.SchemaPrivileges,
privilege.List{},
security.RootUserName(),
),
}).BuildCreatedMutableSchema()
Expand Down
8 changes: 8 additions & 0 deletions pkg/clusterversion/cockroach_versions.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,10 @@ const (
// limits when splitting requests.
TargetBytesAvoidExcess

// ValidateGrantOption checks whether the current user granting privileges to
// another user holds the grant option for those privileges
ValidateGrantOption

// *************************************************
// Step (1): Add new versions here.
// Do not add new versions to a patch release.
Expand Down Expand Up @@ -459,6 +463,10 @@ var versionsSingleton = keyedVersions{
Key: TargetBytesAvoidExcess,
Version: roachpb.Version{Major: 21, Minor: 2, Internal: 4},
},
{
Key: ValidateGrantOption,
Version: roachpb.Version{Major: 21, Minor: 2, Internal: 6},
},

// *************************************************
// Step (2): Add new versions here.
Expand Down
16 changes: 16 additions & 0 deletions pkg/cmd/roachtest/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -2308,6 +2308,22 @@ func (c *clusterImpl) ConnE(ctx context.Context, node int) (*gosql.DB, error) {
return db, nil
}

// ConnEUser returns a SQL connection to the specified node as a specific user
func (c *clusterImpl) ConnEUser(ctx context.Context, node int, user string) (*gosql.DB, error) {
urls, err := c.ExternalPGUrl(ctx, c.Node(node))
if err != nil {
return nil, err
}
//dataSourceName := fmt.Sprintf("%s?user=%s?password=%s", urls[0], user, password)
dataSourceName := strings.Replace(urls[0], "root", user, 1)
fmt.Println("datasourcename is: ", dataSourceName)
db, err := gosql.Open("postgres", dataSourceName)
if err != nil {
return nil, err
}
return db, nil
}

func (c *clusterImpl) MakeNodes(opts ...option.Option) string {
var r option.NodeListOption
for _, o := range opts {
Expand Down
1 change: 1 addition & 0 deletions pkg/cmd/roachtest/cluster/cluster_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type Cluster interface {

Conn(ctx context.Context, node int) *gosql.DB
ConnE(ctx context.Context, node int) (*gosql.DB, error)
ConnEUser(ctx context.Context, node int, user string) (*gosql.DB, error)

// URLs for the Admin UI.

Expand Down
6 changes: 4 additions & 2 deletions pkg/sql/alter_default_privileges.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,12 @@ func (n *alterDefaultPrivilegesNode) startExec(params runParams) error {
privileges := n.n.Grant.Privileges
grantees := n.n.Grant.Grantees
objectType := n.n.Grant.Target
grantOption := n.n.Grant.WithGrantOption
if !n.n.IsGrant {
privileges = n.n.Revoke.Privileges
grantees = n.n.Revoke.Grantees
objectType = n.n.Revoke.Target
grantOption = n.n.Revoke.GrantOptionFor
}

granteeSQLUsernames, err := grantees.ToSQLUsernames(params.p.SessionData(), security.UsernameValidation)
Expand Down Expand Up @@ -165,11 +167,11 @@ func (n *alterDefaultPrivilegesNode) startExec(params runParams) error {
for _, role := range roles {
if n.n.IsGrant {
defaultPrivs.GrantDefaultPrivileges(
role, privileges, granteeSQLUsernames, objectType,
role, privileges, granteeSQLUsernames, objectType, grantOption,
)
} else {
defaultPrivs.RevokeDefaultPrivileges(
role, privileges, granteeSQLUsernames, objectType,
role, privileges, granteeSQLUsernames, objectType, grantOption,
)
}

Expand Down
52 changes: 41 additions & 11 deletions pkg/sql/catalog/catprivilege/default_privilege.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func (d *Mutable) GrantDefaultPrivileges(
privileges privilege.List,
grantees []security.SQLUsername,
targetObject tree.AlterDefaultPrivilegesTargetObject,
withGrantOption bool,
) {
defaultPrivilegesForRole := d.defaultPrivilegeDescriptor.FindOrCreateUser(role)
for _, grantee := range grantees {
Expand All @@ -79,7 +80,7 @@ func (d *Mutable) GrantDefaultPrivileges(
// special privilege cases into real privileges on the PrivilegeDescriptor.
// foldPrivileges converts the real privileges back into flags.
expandPrivileges(defaultPrivilegesForRole, role, &defaultPrivileges, targetObject)
defaultPrivileges.Grant(grantee, privileges)
defaultPrivileges.Grant(grantee, privileges, withGrantOption)
foldPrivileges(defaultPrivilegesForRole, role, &defaultPrivileges, targetObject)
defaultPrivilegesForRole.DefaultPrivilegesPerObject[targetObject] = defaultPrivileges
}
Expand All @@ -91,6 +92,7 @@ func (d *Mutable) RevokeDefaultPrivileges(
privileges privilege.List,
grantees []security.SQLUsername,
targetObject tree.AlterDefaultPrivilegesTargetObject,
grantOptionFor bool,
) {
defaultPrivilegesForRole := d.defaultPrivilegeDescriptor.FindOrCreateUser(role)
for _, grantee := range grantees {
Expand All @@ -99,7 +101,7 @@ func (d *Mutable) RevokeDefaultPrivileges(
// special privilege cases into real privileges on the PrivilegeDescriptor.
// foldPrivileges converts the real privileges back into flags.
expandPrivileges(defaultPrivilegesForRole, role, &defaultPrivileges, targetObject)
defaultPrivileges.Revoke(grantee, privileges, targetObject.ToPrivilegeObjectType())
defaultPrivileges.Revoke(grantee, privileges, targetObject.ToPrivilegeObjectType(), grantOptionFor)
foldPrivileges(defaultPrivilegesForRole, role, &defaultPrivileges, targetObject)

defaultPrivilegesForRole.DefaultPrivilegesPerObject[targetObject] = defaultPrivileges
Expand Down Expand Up @@ -151,18 +153,20 @@ func (d *immutable) CreatePrivilegesFromDefaultPrivileges(
// it as the case where the user has all privileges.
defaultPrivilegesForCreatorRole := descpb.InitDefaultPrivilegesForRole(role)
for _, user := range GetUserPrivilegesForObject(defaultPrivilegesForCreatorRole, targetObject) {
newPrivs.Grant(
newPrivs.GranularGrant(
user.UserProto.Decode(),
privilege.ListFromBitField(user.Privileges, targetObject.ToPrivilegeObjectType()),
privilege.ListFromBitField(user.WithGrantOption, targetObject.ToPrivilegeObjectType()),
)
}
} else {
// If default privileges were defined for the role, we create privileges
// using the default privileges.
for _, user := range GetUserPrivilegesForObject(*defaultPrivilegesForRole, targetObject) {
newPrivs.Grant(
newPrivs.GranularGrant(
user.UserProto.Decode(),
privilege.ListFromBitField(user.Privileges, targetObject.ToPrivilegeObjectType()),
privilege.ListFromBitField(user.WithGrantOption, targetObject.ToPrivilegeObjectType()),
)
}
}
Expand All @@ -173,9 +177,10 @@ func (d *immutable) CreatePrivilegesFromDefaultPrivileges(
defaultPrivilegesForAllRoles, found := d.GetDefaultPrivilegesForRole(descpb.DefaultPrivilegesRole{ForAllRoles: true})
if found {
for _, user := range GetUserPrivilegesForObject(*defaultPrivilegesForAllRoles, targetObject) {
newPrivs.Grant(
newPrivs.GranularGrant(
user.UserProto.Decode(),
privilege.ListFromBitField(user.Privileges, targetObject.ToPrivilegeObjectType()),
privilege.ListFromBitField(user.WithGrantOption, targetObject.ToPrivilegeObjectType()),
)
}
}
Expand All @@ -186,12 +191,26 @@ func (d *immutable) CreatePrivilegesFromDefaultPrivileges(
// For backwards compatibility, also "inherit" privileges from the dbDesc.
// Issue #67378.
if targetObject == tree.Tables || targetObject == tree.Sequences {
//fmt.Println("tables or sequences loop")
for _, u := range databasePrivileges.Users {
newPrivs.Grant(u.UserProto.Decode(), privilege.ListFromBitField(u.Privileges, privilege.Table))
//fmt.Println(u.UserProto.Decode(), u.Privileges, u.WithGrantOption)
// newPrivs.Grant(u.UserProto.Decode(), privilege.ListFromBitField(u.Privileges, privilege.Table), false)
newPrivs.GranularGrant(
u.UserProto.Decode(),
privilege.ListFromBitField(u.Privileges, privilege.Table),
privilege.ListFromBitField(u.WithGrantOption, privilege.Table),
)
}
} else if targetObject == tree.Schemas {
//fmt.Println("schemas loop")
for _, u := range databasePrivileges.Users {
newPrivs.Grant(u.UserProto.Decode(), privilege.ListFromBitField(u.Privileges, privilege.Schema))
//fmt.Println(u.UserProto.Decode(), u.Privileges, u.WithGrantOption)
//newPrivs.Grant(u.UserProto.Decode(), privilege.ListFromBitField(u.Privileges, privilege.Schema), false)
newPrivs.GranularGrant(
u.UserProto.Decode(),
privilege.ListFromBitField(u.Privileges, privilege.Schema),
privilege.ListFromBitField(u.WithGrantOption, privilege.Schema),
)
}
}
return newPrivs
Expand Down Expand Up @@ -249,15 +268,24 @@ func foldPrivileges(
security.PublicRoleName(),
privilege.List{privilege.USAGE},
privilege.Type,
false,
)
}
// ForAllRoles cannot be a grantee, nothing left to do.
if role.ForAllRoles {
return
}
if privileges.HasAllPrivileges(role.Role, targetObject.ToPrivilegeObjectType()) {
setRoleHasAllOnTargetObject(defaultPrivilegesForRole, true, targetObject)
privileges.RemoveUser(role.Role)
// if user.grantoption == 0, then do this next line CHECK THIS LATER
//user := privileges.FindOrCreateUser(role.Role)
//if user.WithGrantOption == 0 {
//setRoleHasAllOnTargetObject(defaultPrivilegesForRole, true, targetObject)
//}
user := privileges.FindOrCreateUser(role.Role) //
if user.WithGrantOption == 0 { //
setRoleHasAllOnTargetObject(defaultPrivilegesForRole, true, targetObject)
privileges.RemoveUser(role.Role)
} //
}
}

Expand All @@ -273,15 +301,15 @@ func expandPrivileges(
targetObject tree.AlterDefaultPrivilegesTargetObject,
) {
if targetObject == tree.Types && GetPublicHasUsageOnTypes(defaultPrivilegesForRole) {
privileges.Grant(security.PublicRoleName(), privilege.List{privilege.USAGE})
privileges.Grant(security.PublicRoleName(), privilege.List{privilege.USAGE}, false)
setPublicHasUsageOnTypes(defaultPrivilegesForRole, false)
}
// ForAllRoles cannot be a grantee, nothing left to do.
if role.ForAllRoles {
return
}
if GetRoleHasAllPrivilegesOnTargetObject(defaultPrivilegesForRole, targetObject) {
privileges.Grant(defaultPrivilegesForRole.GetExplicitRole().UserProto.Decode(), privilege.List{privilege.ALL})
privileges.Grant(defaultPrivilegesForRole.GetExplicitRole().UserProto.Decode(), privilege.List{privilege.ALL}, false)
setRoleHasAllOnTargetObject(defaultPrivilegesForRole, false, targetObject)
}
}
Expand All @@ -299,6 +327,7 @@ func GetUserPrivilegesForObject(
userPrivileges = append(userPrivileges, descpb.UserPrivileges{
UserProto: security.PublicRoleName().EncodeProto(),
Privileges: privilege.USAGE.Mask(),
//GrantOption: privilege.USAGE.Mask(), // CHECK THIS LATER
})
}
// If ForAllRoles is specified, we can return early.
Expand All @@ -312,6 +341,7 @@ func GetUserPrivilegesForObject(
return append(userPrivileges, descpb.UserPrivileges{
UserProto: userProto,
Privileges: privilege.ALL.Mask(),
//GrantOption: privilege.ALL.Mask(), // CHECK THIS LATER
})
}
return userPrivileges
Expand Down
Loading

0 comments on commit 77e27f6

Please sign in to comment.