Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix finding grant conflicts for EXECUTE grants #171

Merged
merged 1 commit into from
Aug 30, 2024
Merged
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
63 changes: 31 additions & 32 deletions mysql/resource_grant.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ type MySQLGrant interface {
SQLRevokeStatement() string
GetUserOrRole() UserOrRole
GrantOption() bool

ConflictsWithGrant(otherGrant MySQLGrant) bool
}

type MySQLGrantWithDatabase interface {
Expand All @@ -57,35 +59,6 @@ type MySQLGrantWithRoles interface {
AppendRoles([]string)
}

func grantsConflict(grantA MySQLGrant, grantB MySQLGrant) bool {
if reflect.TypeOf(grantA) != reflect.TypeOf(grantB) {
return false
}
grantAWithDatabase, aOk := grantA.(MySQLGrantWithDatabase)
grantBWithDatabase, bOk := grantB.(MySQLGrantWithDatabase)
if aOk != bOk {
return false
}
if aOk && bOk {
if grantAWithDatabase.GetDatabase() != grantBWithDatabase.GetDatabase() {
return false
}
}

grantAWithTable, aOk := grantA.(MySQLGrantWithTable)
grantBWithTable, bOk := grantB.(MySQLGrantWithTable)
if aOk != bOk {
return false
}
if aOk && bOk {
if grantAWithTable.GetTable() != grantBWithTable.GetTable() {
return false
}
}

return true
}

type PrivilegesPartiallyRevocable interface {
SQLPartialRevokePrivilegesStatement(privilegesToRevoke []string) string
}
Expand Down Expand Up @@ -175,6 +148,15 @@ func (t *TablePrivilegeGrant) SQLGrantStatement() string {
return stmtSql
}

func (t *TablePrivilegeGrant) ConflictsWithGrant(other MySQLGrant) bool {
otherTyped, ok := other.(*TablePrivilegeGrant)
if !ok {
return false
}
return otherTyped.GetDatabase() == t.GetDatabase() &&
otherTyped.GetTable() == t.GetTable()
}

// containsAllPrivilege returns true if the privileges list contains an ALL PRIVILEGES grant
// this is used because there is special case behavior for ALL PRIVILEGES grants. In particular,
// if a user has ALL PRIVILEGES, we _cannot_ revoke ALL PRIVILEGES, GRANT OPTION because this is
Expand Down Expand Up @@ -273,6 +255,15 @@ func (t *ProcedurePrivilegeGrant) SQLPartialRevokePrivilegesStatement(privileges
return fmt.Sprintf("REVOKE %s ON %s %s.%s FROM %s", strings.Join(privs, ", "), t.ObjectT, t.GetDatabase(), t.GetCallableName(), t.UserOrRole.SQLString())
}

func (t *ProcedurePrivilegeGrant) ConflictsWithGrant(other MySQLGrant) bool {
otherTyped, ok := other.(*ProcedurePrivilegeGrant)
if !ok {
return false
}
return otherTyped.GetDatabase() == t.GetDatabase() &&
otherTyped.GetCallableName() == t.GetCallableName()
}

type RoleGrant struct {
Roles []string
Grant bool
Expand Down Expand Up @@ -315,6 +306,14 @@ func (t *RoleGrant) AppendRoles(roles []string) {
t.Roles = append(t.Roles, roles...)
}

func (t *RoleGrant) ConflictsWithGrant(other MySQLGrant) bool {
otherTyped, ok := other.(*RoleGrant)
if !ok {
return false
}
return otherTyped.GetUserOrRole() == t.GetUserOrRole()
}

func resourceGrant() *schema.Resource {
return &schema.Resource{
CreateContext: CreateGrant,
Expand Down Expand Up @@ -698,7 +697,7 @@ func ImportGrant(ctx context.Context, d *schema.ResourceData, meta interface{})
return nil, fmt.Errorf("failed to showUserGrants in import: %w", err)
}
for _, foundGrant := range grants {
if grantsConflict(desiredGrant, foundGrant) {
if foundGrant.ConflictsWithGrant(desiredGrant) {
res := resourceGrant().Data(nil)
setDataFromGrant(foundGrant, res)
return []*schema.ResourceData{res}, nil
Expand Down Expand Up @@ -769,7 +768,7 @@ func setDataFromGrant(grant MySQLGrant, d *schema.ResourceData) *schema.Resource
func combineGrants(grantA MySQLGrant, grantB MySQLGrant) (MySQLGrant, error) {
// Check if the grants cover the same user, table, database
// If not, throw an error because they are unmergeable
if !grantsConflict(grantA, grantB) {
if !grantA.ConflictsWithGrant(grantB) {
return nil, fmt.Errorf("unable to combine MySQLGrant %s with %s because they don't cover the same table/database/user", grantA, grantB)
}

Expand Down Expand Up @@ -802,7 +801,7 @@ func getMatchingGrant(ctx context.Context, db *sql.DB, desiredGrant MySQLGrant)

// Check if the grants cover the same user, table, database
// If not, continue
if !grantsConflict(desiredGrant, dbGrant) {
if !desiredGrant.ConflictsWithGrant(dbGrant) {
log.Printf("[DEBUG] Skipping grant %#v as it doesn't match %#v", dbGrant, desiredGrant)
continue
}
Expand Down
Loading