Skip to content

Conversation

@jirwin
Copy link
Contributor

@jirwin jirwin commented Jan 24, 2025

Description

Introduces provisioning support. This is accomplished by adding a provisioning configuration per entitlement.

Example configuration:

provisioning:
        vars:
          principal_id: principal.ID
          role_string: "phpSerializeStringArray([resource.ID])"
        grant:
        - |
          DELETE FROM wp_usermeta
          WHERE user_id = ?<principal_id> AND meta_key = 'wp_capabilities'
        - |
          INSERT INTO wp_usermeta (user_id, meta_key, meta_value)
          VALUES (?<principal_id>, 'wp_capabilities', ?<role_string>)
        revoke:
        - |
          DELETE FROM wp_usermeta
          WHERE user_id = ?<principal_id> AND meta_key = 'wp_capabilities'

In this configuration you can define a set of variables that can be CEL expressions. You can then reference these variables by placing ?<variable_name> within the query. During query execution, the expressions will be evaluated and added to the query. The CEL environment for these provisioning queries include: principal.ID, principal.Type, resource.ID, resource.Type and entitlement.ID.

The goal with variables is to allow transformations during sync to be 'reversed' during provisioning. A concrete example of this is in wordpress, the value that needs to be added to grant a role must be a PHP serialized array, but during sync we deserialize the PHP array to get the actual role name to use as the resource ID. In our example above we are able to transform the resource.ID into the array that needs to be written to the database.

For both grant and revoke you can specify a list of queries to run in order. These queries are run in a transaction, and if any fails we roll the transaction back. We need this functionality to support more complex provisioning operations(e.g. multiple tables need to be updated) or in the case of wordpress where the table isn't setup to support Upserting. Since a user may only have 1 role, we must first delete the original role, and then create a new one.

  • Bug fix
  • New feature

return err
}
defer func() {
if err := tx.Rollback(); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will tx.Rollback() error if the transaction has already been committed? If so, would that cause this function to return an error on success?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that the db driver handles this for us, but made it more explicit in fc3935d in case we use a driver that doesn't handle it appropriately.

l.Error("failed to get rows affected", zap.Error(err))
}

l.Debug("query executed", zap.String("query", q), zap.Any("args", qArgs), zap.Int64("rows_affected", rowsAffected))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we want to have a warning message or possibly return an error and rollback if rowsAffected is greater than some threshold? Maybe... 1 by default?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, I've made it so that we will fail and rollback if a query affects more than one row. This is pretty restrictive, but I think I'd prefer to discover a usecase before allowing it. This feels much safer in general. Also done in fc3935d

… fail the transaction if a query affects more than one row
@jirwin jirwin merged commit 2d2a48a into main Jan 24, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants