<commit_before>package mysql

import (
	"fmt"
	"strings"

	"github.com/hashicorp/vault/helper/strutil"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)

const SecretCredsType = "creds"

// Default Revoke and Drop user SQL statment
// Revoking permissions for the user is done before the
// drop, because MySQL explicitly documents that open user connections
// will not be closed. By revoking all grants, at least we ensure
// that the open connection is useless.
// Dropping the user will only affect the next connection
// This is not a prepared statement because not all commands are supported
// 1295: This command is not supported in the prepared statement protocol yet
// Reference https://mariadb.com/kb/en/mariadb/prepare-statement/
const defaultRevokeSQL = `
REVOKE ALL PRIVILEGES, GRANT OPTION FROM '{{name}}'@'%'; 
DROP USER '{{name}}'@'%'
`

func secretCreds(b *backend) *framework.Secret {
	return &framework.Secret{
		Type: SecretCredsType,
		Fields: map[string]*framework.FieldSchema{
			"username": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "Username",
			},

			"password": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "Password",
			},

			"role": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "Role",
			},
		},

		Renew:  b.secretCredsRenew,
		Revoke: b.secretCredsRevoke,
	}
}

func (b *backend) secretCredsRenew(
	req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	// Get the lease information
	lease, err := b.Lease(req.Storage)
	if err != nil {
		return nil, err
	}
	if lease == nil {
		lease = &configLease{}
	}

	f := framework.LeaseExtend(lease.Lease, lease.LeaseMax, b.System())
	return f(req, d)
}

func (b *backend) secretCredsRevoke(
	req *logical.Request, d *framework.FieldData) (*logical.Response, error) {

	// Get the username from the internal data
	usernameRaw, ok := req.Secret.InternalData["username"]
	if !ok {
		return nil, fmt.Errorf("secret is missing username internal data")
	}
	username, ok := usernameRaw.(string)

	// Get our connection
	db, err := b.DB(req.Storage)
	if err != nil {
		return nil, err
	}

	// Get the role name
	roleNameRaw, ok := req.Secret.InternalData["role"]
	roleName, ok := roleNameRaw.(string)

	// init role entry struct
	var role = &roleEntry{}
	if roleName != "" {
		role, err = b.Role(req.Storage, roleName)
		if err != nil {
			return nil, err
		}
	}

	// Check for an empty revokeSQL string
	// set it to a default query if the string is empty
	if role.RevokeSQL == "" {
		role.RevokeSQL = defaultRevokeSQL
	}

	// Start a transaction
	tx, err := db.Begin()
	if err != nil {
		return nil, err
	}
	defer tx.Rollback()

	for _, query := range strutil.ParseArbitraryStringSlice(role.RevokeSQL, ";") {
		query = strings.TrimSpace(query)
		if len(query) == 0 {
			continue
		}

		query = strings.Replace(query, "{{name}}", username, -1)
		_, err = tx.Exec(query)
		if err != nil {
			return nil, err
		}

	}

	// Commit the transaction
	if err := tx.Commit(); err != nil {
		return nil, err
	}
	return nil, nil
}<commit_msg>More resilient around cases of missing role names and using the default when needed.<commit_after>package mysql

import (
	"fmt"
	"strings"

	"github.com/hashicorp/vault/helper/strutil"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)

const SecretCredsType = "creds"

// Default Revoke and Drop user SQL statment
// Revoking permissions for the user is done before the
// drop, because MySQL explicitly documents that open user connections
// will not be closed. By revoking all grants, at least we ensure
// that the open connection is useless.
// Dropping the user will only affect the next connection
// This is not a prepared statement because not all commands are supported
// 1295: This command is not supported in the prepared statement protocol yet
// Reference https://mariadb.com/kb/en/mariadb/prepare-statement/
const defaultRevokeSQL = `
REVOKE ALL PRIVILEGES, GRANT OPTION FROM '{{name}}'@'%'; 
DROP USER '{{name}}'@'%'
`

func secretCreds(b *backend) *framework.Secret {
	return &framework.Secret{
		Type: SecretCredsType,
		Fields: map[string]*framework.FieldSchema{
			"username": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "Username",
			},

			"password": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "Password",
			},

			"role": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "Role",
			},
		},

		Renew:  b.secretCredsRenew,
		Revoke: b.secretCredsRevoke,
	}
}

func (b *backend) secretCredsRenew(
	req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	// Get the lease information
	lease, err := b.Lease(req.Storage)
	if err != nil {
		return nil, err
	}
	if lease == nil {
		lease = &configLease{}
	}

	f := framework.LeaseExtend(lease.Lease, lease.LeaseMax, b.System())
	return f(req, d)
}

func (b *backend) secretCredsRevoke(
	req *logical.Request, d *framework.FieldData) (*logical.Response, error) {

	// Get the username from the internal data
	usernameRaw, ok := req.Secret.InternalData["username"]
	if !ok {
		return nil, fmt.Errorf("secret is missing username internal data")
	}
	username, ok := usernameRaw.(string)

	// Get our connection
	db, err := b.DB(req.Storage)
	if err != nil {
		return nil, err
	}

	// Get the role name
	// we may not always have role data in the secret InternalData
	// so don't exit if the roleNameRaw fails. Instead it is set
	// as an empty string
	var roleName string
	roleNameRaw, ok := req.Secret.InternalData["role"]
	if !ok {
		roleName = ""
	} else {
		roleName, ok = roleNameRaw.(string)
	}
	// init default revoke sql string.
	// this will replaced by a user provided one if one exists
	// otherwise this is what will be used when lease is revoked
	revokeSQL := defaultRevokeSQL

	// if we were successful in finding a role name
	// create role entry from that name
	if roleName != "" {
		role, err = b.Role(req.Storage, roleName)
		if err != nil {
			return nil, err
		}
		// Check for a revokeSQL string
		// if one exists use that instead of the default
		if role.RevokeSQL != "" {
			revokeSQL = role.RevokeSQL
		}
	}

	// Start a transaction
	tx, err := db.Begin()
	if err != nil {
		return nil, err
	}
	defer tx.Rollback()

	for _, query := range strutil.ParseArbitraryStringSlice(revokeSQL, ";") {
		query = strings.TrimSpace(query)
		if len(query) == 0 {
			continue
		}

		// This is not a prepared statement because not all commands are supported
		// 1295: This command is not supported in the prepared statement protocol yet
		// Reference https://mariadb.com/kb/en/mariadb/prepare-statement/
		query = strings.Replace(query, "{{name}}", username, -1)
		_, err = tx.Exec(query)
		if err != nil {
			return nil, err
		}

	}

	// Commit the transaction
	if err := tx.Commit(); err != nil {
		return nil, err
	}
	return nil, nil
}
{'fraction_non_alphanumeric': 0.12017593643586832, 'fraction_numerical': 0.0022701475595913734, 'mean_word_length': 3.4840966921119594, 'pattern_counts': {'":': 6, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4136062', 'n_tokens_mistral': 2455, 'n_tokens_neox': 2265, 'n_words': 734}