mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-04 14:46:19 +08:00 
			
		
		
		
	Fix #16481: Fix RBAC MSSQL trigger
This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							2d15c484ef
						
					
				
				
					commit
					3bc919bcbd
				
			@ -12,6 +12,7 @@ Yii Framework 2 Change Log
 | 
			
		||||
- Bug #17810: Fix EachValidator crashing with uninitialized typed properties (ricardomm85)
 | 
			
		||||
- Bug #17942: Fix for `DbCache` loop in MySQL `QueryBuilder` (alex-code)
 | 
			
		||||
- Bug #17960: Fix unsigned primary key type mapping for SQLite (bizley)
 | 
			
		||||
- Bug #16481: Fix RBAC MSSQL trigger (achretien)
 | 
			
		||||
- Enh #17758: `Query::withQuery()` can be used for CTE (sartor)
 | 
			
		||||
- Bug #17974: Fix ActiveRelationTrait compatibility with PHP 7.4 (Ximich)
 | 
			
		||||
- Bug #17991: Improve `yii\db\Connection` master and slave failover, no connection attempt was made when all servers are marked as unavailable  (cebe)
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,167 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * @link http://www.yiiframework.com/
 | 
			
		||||
 * @copyright Copyright (c) 2008 Yii Software LLC
 | 
			
		||||
 * @license http://www.yiiframework.com/license/
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use yii\base\InvalidConfigException;
 | 
			
		||||
use yii\db\Migration;
 | 
			
		||||
use yii\db\Query;
 | 
			
		||||
use yii\rbac\DbManager;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Fix MSSQL trigger.
 | 
			
		||||
 *
 | 
			
		||||
 * @see https://github.com/yiisoft/yii2/pull/17966
 | 
			
		||||
 *
 | 
			
		||||
 * @author Aurelien Chretien <chretien.aurelien@gmail.com>
 | 
			
		||||
 * @since 2.0.35
 | 
			
		||||
 */
 | 
			
		||||
class m200409_110543_rbac_update_mssql_trigger extends Migration
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * @throws yii\base\InvalidConfigException
 | 
			
		||||
     * @return DbManager
 | 
			
		||||
     */
 | 
			
		||||
    protected function getAuthManager()
 | 
			
		||||
    {
 | 
			
		||||
        $authManager = Yii::$app->getAuthManager();
 | 
			
		||||
        if (!$authManager instanceof DbManager) {
 | 
			
		||||
            throw new InvalidConfigException('You should configure "authManager" component to use database before executing this migration.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $authManager;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function findForeignKeyName($table, $column, $referenceTable, $referenceColumn)
 | 
			
		||||
    {
 | 
			
		||||
        return (new Query())
 | 
			
		||||
            ->select(['OBJECT_NAME(fkc.constraint_object_id)'])
 | 
			
		||||
            ->from(['fkc' => 'sys.foreign_key_columns'])
 | 
			
		||||
            ->innerJoin(['c' => 'sys.columns'], 'fkc.parent_object_id = c.object_id AND fkc.parent_column_id = c.column_id')
 | 
			
		||||
            ->innerJoin(['r' => 'sys.columns'], 'fkc.referenced_object_id = r.object_id AND fkc.referenced_column_id = r.column_id')
 | 
			
		||||
            ->where(
 | 
			
		||||
                [
 | 
			
		||||
                    'AND',
 | 
			
		||||
                    ['fkc.parent_object_id' => $this->db->schema->getRawTableName($table)],
 | 
			
		||||
                    ['fkc.referenced_object_id' => $this->db->schema->getRawTableName($referenceTable)],
 | 
			
		||||
                    ['c.name' => $column],
 | 
			
		||||
                    ['r.name' => $referenceColumn],
 | 
			
		||||
                ]
 | 
			
		||||
            )->scalar($this->db);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return bool
 | 
			
		||||
     */
 | 
			
		||||
    protected function isMSSQL()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->db->driverName === 'mssql' || $this->db->driverName === 'sqlsrv' || $this->db->driverName === 'dblib';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function up()
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->isMSSQL()) {
 | 
			
		||||
            $authManager = $this->getAuthManager();
 | 
			
		||||
            $this->db = $authManager->db;
 | 
			
		||||
            $schema = $this->db->getSchema()->defaultSchema;
 | 
			
		||||
            $triggerSuffix = $this->db->schema->getRawTableName($authManager->itemChildTable);
 | 
			
		||||
 | 
			
		||||
            $this->execute("DROP TRIGGER {$schema}.trigger_{$triggerSuffix};");
 | 
			
		||||
 | 
			
		||||
            $this->execute("CREATE TRIGGER {$schema}.trigger_delete_{$triggerSuffix}
 | 
			
		||||
            ON {$schema}.{$authManager->itemTable}
 | 
			
		||||
            INSTEAD OF DELETE
 | 
			
		||||
            AS
 | 
			
		||||
            BEGIN
 | 
			
		||||
                  DELETE FROM {$schema}.{$authManager->itemChildTable} WHERE parent IN (SELECT name FROM deleted) OR child IN (SELECT name FROM deleted);
 | 
			
		||||
                  DELETE FROM {$schema}.{$authManager->itemTable} WHERE name IN (SELECT name FROM deleted);
 | 
			
		||||
            END;"
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            $foreignKey = $this->findForeignKeyName($authManager->itemChildTable, 'child', $authManager->itemTable, 'name');
 | 
			
		||||
            $this->execute("CREATE TRIGGER {$schema}.trigger_update_{$triggerSuffix}
 | 
			
		||||
            ON {$schema}.{$authManager->itemTable}
 | 
			
		||||
            INSTEAD OF UPDATE
 | 
			
		||||
            AS
 | 
			
		||||
                DECLARE @old_name NVARCHAR(64) = (SELECT name FROM deleted)
 | 
			
		||||
                DECLARE @new_name NVARCHAR(64) = (SELECT name FROM inserted)
 | 
			
		||||
            BEGIN
 | 
			
		||||
                IF @old_name <> @new_name
 | 
			
		||||
                BEGIN
 | 
			
		||||
                    ALTER TABLE {$authManager->itemChildTable} NOCHECK CONSTRAINT {$foreignKey};
 | 
			
		||||
                    UPDATE {$authManager->itemChildTable} SET child = @new_name WHERE child = @old_name;
 | 
			
		||||
                END
 | 
			
		||||
            UPDATE {$authManager->itemTable}
 | 
			
		||||
            SET name = (SELECT name FROM inserted),
 | 
			
		||||
            type = (SELECT type FROM inserted),
 | 
			
		||||
            description = (SELECT description FROM inserted),
 | 
			
		||||
            rule_name = (SELECT rule_name FROM inserted),
 | 
			
		||||
            data = (SELECT data FROM inserted),
 | 
			
		||||
            created_at = (SELECT created_at FROM inserted),
 | 
			
		||||
            updated_at = (SELECT updated_at FROM inserted)
 | 
			
		||||
            WHERE name IN (SELECT name FROM deleted)
 | 
			
		||||
            IF @old_name <> @new_name
 | 
			
		||||
                BEGIN
 | 
			
		||||
                    ALTER TABLE {$authManager->itemChildTable} CHECK CONSTRAINT {$foreignKey};
 | 
			
		||||
                END
 | 
			
		||||
            END;"
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritdoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function down()
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->isMSSQL()) {
 | 
			
		||||
            $authManager = $this->getAuthManager();
 | 
			
		||||
            $this->db = $authManager->db;
 | 
			
		||||
            $schema = $this->db->getSchema()->defaultSchema;
 | 
			
		||||
            $triggerSuffix = $this->db->schema->getRawTableName($authManager->itemChildTable);
 | 
			
		||||
 | 
			
		||||
            $this->execute("DROP TRIGGER {$schema}.trigger_update_{$triggerSuffix};");
 | 
			
		||||
            $this->execute("DROP TRIGGER {$schema}.trigger_delete_{$triggerSuffix};");
 | 
			
		||||
 | 
			
		||||
            $this->execute("CREATE TRIGGER {$schema}.trigger_auth_item_child
 | 
			
		||||
            ON {$schema}.{$authManager->itemTable}
 | 
			
		||||
            INSTEAD OF DELETE, UPDATE
 | 
			
		||||
            AS
 | 
			
		||||
            DECLARE @old_name VARCHAR (64) = (SELECT name FROM deleted)
 | 
			
		||||
            DECLARE @new_name VARCHAR (64) = (SELECT name FROM inserted)
 | 
			
		||||
            BEGIN
 | 
			
		||||
            IF COLUMNS_UPDATED() > 0
 | 
			
		||||
                BEGIN
 | 
			
		||||
                    IF @old_name <> @new_name
 | 
			
		||||
                    BEGIN
 | 
			
		||||
                        ALTER TABLE {$authManager->itemChildTable} NOCHECK CONSTRAINT FK__auth_item__child;
 | 
			
		||||
                        UPDATE {$authManager->itemChildTable} SET child = @new_name WHERE child = @old_name;
 | 
			
		||||
                    END
 | 
			
		||||
                UPDATE {$authManager->itemTable}
 | 
			
		||||
                SET name = (SELECT name FROM inserted),
 | 
			
		||||
                type = (SELECT type FROM inserted),
 | 
			
		||||
                description = (SELECT description FROM inserted),
 | 
			
		||||
                rule_name = (SELECT rule_name FROM inserted),
 | 
			
		||||
                data = (SELECT data FROM inserted),
 | 
			
		||||
                created_at = (SELECT created_at FROM inserted),
 | 
			
		||||
                updated_at = (SELECT updated_at FROM inserted)
 | 
			
		||||
                WHERE name IN (SELECT name FROM deleted)
 | 
			
		||||
                IF @old_name <> @new_name
 | 
			
		||||
                    BEGIN
 | 
			
		||||
                        ALTER TABLE {$authManager->itemChildTable} CHECK CONSTRAINT FK__auth_item__child;
 | 
			
		||||
                    END
 | 
			
		||||
                END
 | 
			
		||||
                ELSE
 | 
			
		||||
                    BEGIN
 | 
			
		||||
                        DELETE FROM {$schema}.{$authManager->itemChildTable} WHERE parent IN (SELECT name FROM deleted) OR child IN (SELECT name FROM deleted);
 | 
			
		||||
                        DELETE FROM {$schema}.{$authManager->itemTable} WHERE name IN (SELECT name FROM deleted);
 | 
			
		||||
                    END
 | 
			
		||||
            END;");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user