Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / database / SQL-Server

How to migrate DevExpress XAF SecuritySystemUser to PermissionPolicyUser

5.00/5 (7 votes)
8 Nov 2016CPOL5 min read 19K  
Instructions on how to migrate existing XAF security system to new permission policy.

Introduction

I spent time migrating XAF existing security system to new much improved policy permission system and I would like to share my experience and code for anyone else who might decide for this endevour.

My application is still in development and uses security system quite heavily which is whay I decided to go to improved security system.
Since this scripts will turn your database upside down and back (hopefully) I strongly advise you not to run this in production, but if you really must - then I will state the obvious - make sure you have backups. Several of them. On different media in different buildings preferrably on different continents. 
Also, all scripts are Microsoft SQL Server specific, so if you are using another database you will need to rework all metadata queries.

With that said, let us dig in.

Using the code

All code can be implemented using existing XAF update mechanisam:

  • UpdateDatabaseBeforeUpdateSchema - which executes before XAF tries to push class changes into database and
  • UpdateDatabaseAfterUpdateSchema which executes after XAF is done pusing class changes and we get a chance to perform additional post-update tasks.

I had two different kind of columns that I had to deal with which will provide as a good example if you will have similar scenarios:

  1. In my inital version of application I created a common parent class which had UserCreated and UserModified uniqueidentifier properties connected to SecuritySystemUser. I decided to change those properties to nvarchar and to keep usernames instead
  2. User properties that I wanted to keep and migrate to PermissionPolicyUser (for instance - Employee.SystemUser column)

Entire process can be divided into following steps:

  1. In UpdateDatabaseBeforeUpdateSchema change all uniqueidentifier columns to nvarchar and update new columns with username
  2. Also, rename all existing columns (and their foreign keys and indices) which will allow XAF to create new columns properly connected to PermissionPolicyUser
  3. Allow XAF to do it's own thing
  4. In UpdateDatabaseAfterUpdateSchema - Copy users, roles and permission from old tables to new
  5. And finally run script that will update new columns with previous data and delete old columns/relationships/indices

I wrapped all scripts into stored procedures which makes it easier and safer to call from XAF (rather than multi SQL statements). Also, each stored procedure is saved as separate file and added to my agnostic module as embedded resources so I can access them during XAF update procedure.

Step 1 - prepare scripts and execute them in UpdateDatabaseBeforeUpdateSchema

Following code will load embedded script

C#
if (CurrentDBVersion < new Version("3.5.3.105"))
{
    // This updates XAF splash screen
    UpdateStatus("InitSecurity", "Migrate users", "This action could take several minutes ...");

    batchSql = Func.GetSqlScript(this.GetType(), "Link.Module.DatabaseUpdate.Script.{0}.sql", "sp_sys_ConvertUsersToString", "");

    foreach (string sql in Func.GetNonQueryFromBatch(batchSql))
    {
        if (!string.IsNullOrWhiteSpace(sql)) ExecuteNonQueryCommand(sql, false);
    }

    using (var conn = DB.DBCommon.NewMSSqlConnection)
    {
        conn.Open();

        using (SqlCommand cmd = new SqlCommand(@"EXEC dbo.sp_sys_ConvertUsersToString", conn))
        {
            cmd.CommandTimeout = 0;
            cmd.ExecuteNonQuery();
        }

        conn.Close();
    }
}

We are using helper functions GetSqlScript (which loads embedded resource script) and GetNonQueryFromBatch (which parses individual blocks between GO statements), so here they are:

C#
public static string GetSqlScript(Type type, string ns, string scriptName, string version)
{
    Assembly _assembly = type.Assembly;
    StreamReader _textStreamReader;
    string text = "";
    try
    {
        string resourceName = "";
        if (version == "") resourceName = string.Format(ns, scriptName);
        else resourceName = string.Format(ns, version, scriptName);

        if (resourceName != "")
        {
            using (_textStreamReader = new StreamReader(_assembly.GetManifestResourceStream(resourceName)))
            {
                text = _textStreamReader.ReadToEnd();
                _textStreamReader.Close();
            }
        }
    }
    catch
    {
        throw new Exception("Error accessing resources!");
    }

    return text;
}

public static List<string> GetNonQueryFromBatch(string batchSql)
{
    List<string> result = new List<string>();

    string sql = string.Empty;
    batchSql += "\nGO";   // make sure last batch is executed.
    foreach (string line in batchSql.Split(new string[2] { "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries))
    {
        if (line.ToUpperInvariant().Trim() == "GO")
        {
            result.Add(sql);
            sql = string.Empty;
        }
        else
        {
            sql += line + "\n";
        }
    }

    return result;
}

And here is the code for sp_sys_ConvertUsersToString stored procedure (see comments for more details):

SQL
ALTER PROCEDURE [dbo].[sp_sys_ConvertUsersToString]
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    BEGIN TRAN T1;

    DECLARE @sql nvarchar(MAX)
    DECLARE @sch nvarchar(50)
    DECLARE @tbl nvarchar(200)
    DECLARE @col nvarchar(200)
    DECLARE @fk nvarchar(200)

    -- I had extra indices in all tables (seemed like a good idea in the time) but I do not need them any more, so I decided to use this chance
    -- and remove them.
    -- This query generates SQL that will drop all indexes with LinkID in it's name
    -- This is not directly related to subject, but if you have some extra work you have been waiting to do - this is a good chance to do it
    select @sql = (
    select 'DROP INDEX ' + o.name + '.' + i.name + char(10) as [text()]
      from sys.indexes i join sys.objects o on  i.object_id=o.object_id
      where i.name LIKE '%LinkID%'
      for xml path('')
    )

    -- PRINT @sql
    EXEC sp_executesql @sql

    -- Following query will locate all columns in all tables which reference SecuritySystemUser table - except columns in those tables which are actually
    -- part of Security System - we do not want to touch those or we might mess everything up
    DECLARE cur_tbl CURSOR FOR
    select
        tab1.name AS [table],
        sch.name AS [schema_name],
        col1.name AS [column],
        obj.name AS FK_NAME
    FROM sys.foreign_key_columns fkc
    INNER JOIN sys.objects obj
        ON obj.object_id = fkc.constraint_object_id
    INNER JOIN sys.tables tab1
        ON tab1.object_id = fkc.parent_object_id
    INNER JOIN sys.schemas sch
        ON tab1.schema_id = sch.schema_id
    INNER JOIN sys.columns col1
        ON col1.column_id = parent_column_id AND col1.object_id = tab1.object_id
    INNER JOIN sys.tables tab2
        ON tab2.object_id = fkc.referenced_object_id
    INNER JOIN sys.columns col2
    ON col2.column_id = referenced_column_id AND col2.object_id = tab2.object_id
    WHERE
        tab2.name = 'SecuritySystemUser'
        and not tab1.name like '%SecuritySystem%'

    -- This is a helper table variable which will hold all related indices
    DECLARE @idxtbl TABLE (
        index_name nvarchar(200),
        index_description nvarchar(200),
        index_keys nvarchar(200)
    )
    DECLARE @sql2 nvarchar(max)

    OPEN cur_tbl
    FETCH NEXT FROM cur_tbl INTO @tbl, @sch, @col, @fk

    WHILE @@FETCH_STATUS = 0
    BEGIN
        -- There are two kinds of columns in my database
        -- First, those that I wanted to turn into nvarchar (to store username) instead of uniqueidentifier (User Oid)
        -- I decided it is more convenient to store user name in the first place
        -- All those columns in my database are called UserCreated and UserModified

        IF @col in ('UserCreated', 'UserModified')
        BEGIN
            -- First I create SQL which will add new nvarchar column with Str sufix (which will temporarly hold username)
            SET @sql = 'ALTER TABLE ' + @sch + '.' + @tbl + ' ADD ' + @col + 'Str nvarchar(100) '
             EXEC sp_executesql @sql
            --PRINT @sql

            -- Next SQL will actually update this new Str column by making a join to SecuritySystemUser
            SET @sql = ' UPDATE q SET q.' + @col + 'Str = u.UserName FROM ' + @sch + '.' + @tbl + ' q INNER JOIN dbo.SecuritySystemUser u on u.Oid = q.' + @col
             EXEC sp_executesql @sql
            --PRINT @sql

            -- Then I drop constraint for uniqueidentifier column
            SET @sql = 'ALTER TABLE ' + @sch + '.' + @tbl + ' DROP CONSTRAINT ' + @fk
             EXEC sp_executesql @sql
            --PRINT @sql

            DELETE FROM @idxtbl

            -- This will load all indices for given table into table variable
            INSERT INTO @idxtbl
            EXEC sys.sp_helpindex @objname = @tbl

            -- After constraint is dropped, I also drop all indices that Oid column might have
            -- I use index_keys property to only filter indices defined for Oid column
            SELECT
                @sql = (
                    select                 
                        ' DROP INDEX ' + @sch + '.' + @tbl + '.' + index_name  + CHAR(10)
                    from @idxtbl
                    where index_keys = @col
                    for xml path('')
                    )

             EXEC sp_executesql @sql
            --PRINT @sql

            -- ThenI drop Oid column
            SET @sql = ' ALTER TABLE ' + @sch + '.' + @tbl + ' DROP COLUMN ' + @col
             EXEC sp_executesql @sql
            --PRINT @sql

            -- And finally I rename Str column to original name of the column
            SET @sql = ' EXEC sp_rename ''' + @sch + '.' + @tbl + '.' + @col + 'Str'', ''' + @col + ''', ''COLUMN'''  
             EXEC sp_executesql @sql
            --PRINT @sql
        END
        ELSE
        BEGIN
            -- Second type of columns are User Oids I actually want to keep and convert to PermissionPolicyUser
            -- But, in order to do that - I have to rename all existing columns, relationship or indicies
            -- I do that because I want XAF to re-create this columns with relationship to PermissionPolicyUser
            -- But I do not want to delete them, because I want to transfer data after XAF does it's job
            -- That is why I rename all columns I want to keep (renaming both relationship and indices are also
            -- required, otherwise XAF will fail to update with error that foreign key or index already exists)
            SET @sql =
                -- This SQL will add Old sufix to column I want to keep
                'EXEC sp_rename ''' + @sch + '.' + @tbl + '.' + @col + ''', ''' + @col + 'Old'', ''COLUMN''; '  + CHAR(10) +
                
                -- This SQL will rename foreign key related to column I want to keep
                ' EXEC sp_rename ''' + @fk + ''',''' + @fk + 'Old'', ''OBJECT'';' + CHAR(10)

            DELETE FROM @idxtbl

            -- Same as in previous case, I load indices for related table
            INSERT INTO @idxtbl
            EXEC sys.sp_helpindex @objname = @tbl

            -- And create SQL to rename all indices connected to column I want to keep
            SELECT
                @sql2 = (
                    select                 
                        ' EXEC sp_rename ''' + @sch + '.' + @tbl + '.' + index_name + ''', ''' + index_name + 'Old'', ''INDEX''; '  + CHAR(10)
                    from @idxtbl
                    where index_keys = @col
                    for xml path('')
                    )

            SET @sql = @sql + @sql2

             EXEC sp_executesql @sql
            --PRINT @sql
        END
        FETCH NEXT FROM cur_tbl INTO @tbl, @sch, @col, @fk
    END

    CLOSE cur_tbl
    DEALLOCATE cur_tbl

    COMMIT TRAN T1
    END
GO

So, in UpdateDatabaseBeforeUpdateSchema we both update stored procedure and execute it. You might notice I use ALTER PROCEDURE instead of CREATE PROCEDURE.
This is because I have a script which executes on beginning of UpdateDatabaseBeforeUpdateSchema and creates any procedures, functions and views which does not exists with empty bodies.
This makes "dependency hell" hell of a less of a hell :-) and ensures every procedure / function / view exists which allows me to always use ALTER scripts and not bother too much.

Here is a snippet from this script:

SQL
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_sys_ConvertUsersToString]') AND type = N'P')
    EXEC sp_executesql N'CREATE PROCEDURE [dbo].[sp_sys_ConvertUsersToString] AS BEGIN SET NOCOUNT ON END'
GO

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_sys_MigrateUsersToPermissionPolicy]') AND type = N'P')
    EXEC sp_executesql N'CREATE PROCEDURE [dbo].[sp_sys_MigrateUsersToPermissionPolicy] AS BEGIN SET NOCOUNT ON END'
GO

Of course, there are other ways to do this, but I found this to be most convenient and reduces the need to worry about dependencies to bare minimum.

Step 2 - XAF doing it's own magic

After above code executes and I handled columns I wanted to convert to nvarchar and keep user names instead of user oids, I need to allow XAF to push all class changes and actually creates new permission policy tables and also creates columns and relationship in classes where I used user which I wanted to migrate (like Employee.SystemUser)

This is handled by framework itself and starts automatically after UpdateDatabaseBeforeUpdateSchema completes it's execution.

Step 3 - Post update migration

Ok, we are now in UpdateDatabaseAfterUpdateSchema.

It is important to note that you need to switch to new security system. You can do that either using module designer on your application (WinApplication.cs) setting PermissionPolicyUser to UserType and PermissionPolicyRole to RoleType (see XAF documentation for more details) or you can do it in code like this:

C#
public static WindowsFormsApplication CreateApplication()

{

   WindowsFormsApplication winApplication = new WindowsFormsApplication();

   winApplication.Security =

      new SecurityStrategyComplex(

         typeof(DevExpress.Persistent.BaseImpl.PermissionPolicy.PermissionPolicyUser),

         typeof(DevExpress.Persistent.BaseImpl.PermissionPolicy.PermissionPolicyRole),

         new WinChangeDatabaseStandardAuthentication()); // This is my authentication type which allows database selection on login, so you will want to use standard XAF authentication here

}

New tables and columns are created and we are left to migrate data from old renamed columns.
To do that, I am using another stored procedure named sp_sys_MigrateUsersToPermissionPolicy. I use same system - procedure in a file addes as embedded resource which I then load and execute.
Here is UpdateDatabaseAfterUpdateSchema code.

C#
if (CurrentDBVersion < new Version("3.5.3.107"))
{
    UpdateStatus("InitSecurity", "Migrate users", "This action might take several minutes ...");

    // We need to manually transfer users because C# scripts can not migrate passwords
    ExecuteNonQueryCommand(@"
INSERT INTO PermissionPolicyUser (Oid, UserName, StoredPassword, ChangePasswordOnFirstLogon, IsActive)
SELECT NEWID(), UserName, StoredPassword, ChangePasswordOnFirstLogon, IsActive
FROM SecuritySystemUser
WHERE GCRecord IS NULL", false);

    MySecurity.ConvertToNewSecurityModel(this, ObjectSpace);

    batchSql = Func.GetSqlScript(this.GetType(), "Link.Module.DatabaseUpdate.Script.{0}.sql", "sp_sys_MigrateUsersToPermissionPolicy", "");
    foreach (string sql in Func.GetNonQueryFromBatch(batchSql))
    {
        if (!string.IsNullOrWhiteSpace(sql)) ExecuteNonQueryCommand(sql, false);
    }

    using (var conn = DB.DBCommon.NewMSSqlConnection)
    {
        conn.Open();

        using (SqlCommand cmd = new SqlCommand(@"EXEC dbo.sp_sys_MigrateUsersToPermissionPolicy", conn))
        {
            cmd.CommandTimeout = 0;
            cmd.ExecuteNonQuery();
        }

        conn.Close();
    }
}

So, again we update procedure code and then executing, but first we migrate users using SQL because C# code can not move passwords.

Here is sp_sys_MigrateUsersToPermissionPolicy procedure (see comments for more info):

SQL
ALTER PROCEDURE [dbo].[sp_sys_MigrateUsersToPermissionPolicy]
AS
BEGIN
    -- This procedure needs to be executed AFTER XAF updates database

    DECLARE @sql nvarchar(MAX)
    DECLARE @sch nvarchar(50)
    DECLARE @tbl nvarchar(200)
    DECLARE @fk nvarchar(200)
    DECLARE @col nvarchar(200)

    -- We fetch all columns that are referencing old SecuritySystemUser
    -- This are basically all columns we renamed in previous step
    -- Just to sleep tighter, we exclude any UserCreated and UserModified columns
    -- Technically, those references should not exists any more, but anything for better sleep, right?
    DECLARE cur_tbl2 CURSOR FOR
    select
        tab1.name AS [table],
        sch.name AS [schema_name],
        obj.name AS FK_NAME,
        col1.name AS [column]
    FROM sys.foreign_key_columns fkc
    INNER JOIN sys.objects obj
        ON obj.object_id = fkc.constraint_object_id
    INNER JOIN sys.tables tab1
        ON tab1.object_id = fkc.parent_object_id
    INNER JOIN sys.schemas sch
        ON tab1.schema_id = sch.schema_id
    INNER JOIN sys.columns col1
        ON col1.column_id = parent_column_id AND col1.object_id = tab1.object_id
    INNER JOIN sys.tables tab2
        ON tab2.object_id = fkc.referenced_object_id
    INNER JOIN sys.columns col2
    ON col2.column_id = referenced_column_id AND col2.object_id = tab2.object_id
    WHERE
        tab2.name = 'SecuritySystemUser'
        and not tab1.name like '%SecuritySystem%'
        and (
            col1.name not in ('UserCreated', 'UserModified')
        )

    -- Table variable to hold related indices
    DECLARE @idxtbl TABLE (
        index_name nvarchar(200),
        index_description nvarchar(200),
        index_keys nvarchar(200)
    )
    DECLARE @sql2 nvarchar(max)

    OPEN cur_tbl2
    FETCH NEXT FROM cur_tbl2 INTO @tbl, @sch, @fk, @col

    WHILE @@FETCH_STATUS = 0
    BEGIN
        SET @sql = ''

        -- Here we check if XAF actually created new columns properly
        -- If it did, we create SQL which will join to SecuritySystemUser using old column Oid and then using UserName join to new PermissionPolicyUser
        -- and update new column wich matching Oid - hence migrating old user to new one
        IF COL_LENGTH(@tbl, REPLACE(@col, 'Old', '')) IS NOT NULL    
            SET @sql =         
                'UPDATE q SET q.' + REPLACE(@col, 'Old', '') + ' = pu.Oid FROM ' + @sch + '.' + @tbl + ' q INNER JOIN dbo.SecuritySystemUser u on u.Oid = q.' + @col + ' INNER JOIN PermissionPolicyUser pu ON pu.UserName = u.UserName; ' + CHAR(10)

        -- After that we drop constraint from Old column
        SET @sql = @sql + ' ALTER TABLE ' + @sch + '.' + @tbl + ' DROP CONSTRAINT ' + @fk + '; ' + CHAR(10)

        DELETE FROM @idxtbl

        INSERT INTO @idxtbl
        EXEC sys.sp_helpindex @objname = @tbl

        -- And also all related indicies
        SELECT
            @sql2 = (
                select                 
                    ' DROP INDEX ' + @tbl + '.' + index_name + '; ' + CHAR(10)
                from @idxtbl
                where index_keys = @col
                for xml path('')
                )

        SET @sql = @sql + @sql2

        -- Finally - we drop Old column and migration is done
        SET @sql = @sql +
            ' ALTER TABLE ' + @sch + '.' + @tbl + ' DROP COLUMN ' + @col + '; ' + CHAR(10)
    
        EXEC sp_executesql @sql
        -- PRINT @sql
    
        FETCH NEXT FROM cur_tbl2 INTO @tbl, @sch, @fk, @col
    END

    CLOSE cur_tbl2
    DEALLOCATE cur_tbl2
END
GO

And finally - here is the code kindly provided by DevExpress (slightly modified since I am actuall migrating users in SQL which original code did not support) wrapped in ConvertToNewSecurityModel:

C#
public static void ConvertToNewSecurityModel(ModuleUpdater updater, IObjectSpace objectSpace)
{
    using (MySecurity mySecurity = new MySecurity(updater, objectSpace, updateAll: false))
    {
        foreach (SecuritySystemUser securitySystemUser in objectSpace.GetObjects<SecuritySystemUser>())
        {
            mySecurity.CopyUser(securitySystemUser);
        }
        foreach (SecuritySystemRole PermissionPolicyRole in objectSpace.GetObjects<SecuritySystemRole>())
        {
            mySecurity.CopyRole(PermissionPolicyRole, null);
        }
        objectSpace.CommitChanges();
    }
}

private void CopyUser(SecuritySystemUser securitySystemUser)
{
    PermissionPolicyUser permissionPolicyUser = ObjectSpace.FindObject<PermissionPolicyUser>(new BinaryOperator("UserName", securitySystemUser.UserName));
    if (permissionPolicyUser == null)
    {
        permissionPolicyUser = ObjectSpace.CreateObject<PermissionPolicyUser>();
        permissionPolicyUser.UserName = securitySystemUser.UserName;
        permissionPolicyUser.IsActive = securitySystemUser.IsActive;
        permissionPolicyUser.SetPassword("123");
        permissionPolicyUser.ChangePasswordOnFirstLogon = securitySystemUser.ChangePasswordOnFirstLogon;
    }

    foreach (SecuritySystemRole securitySystemRole in securitySystemUser.Roles)
    {
        CopyRole(securitySystemRole, permissionPolicyUser);
    }

}
private void CopyRole(SecuritySystemRole securitySystemRole, PermissionPolicyUser permissionPolicyUser)
{
    PermissionPolicyRole permissionPolicyRole = ObjectSpace.FindObject<PermissionPolicyRole>(new BinaryOperator("Name", securitySystemRole.Name));
    if (permissionPolicyRole == null)
    {
        permissionPolicyRole = ObjectSpace.CreateObject<PermissionPolicyRole>();
        permissionPolicyRole.Name = securitySystemRole.Name;
        permissionPolicyRole.PermissionPolicy = SecurityPermissionPolicy.DenyAllByDefault;
        permissionPolicyRole.IsAdministrative = securitySystemRole.IsAdministrative;
        permissionPolicyRole.CanEditModel = securitySystemRole.CanEditModel;
        foreach (SecuritySystemTypePermissionObject securitySystemTypePermissionObject in securitySystemRole.TypePermissions)
        {
            CopyTypePermissions(securitySystemTypePermissionObject, securitySystemRole, permissionPolicyRole);
        }
        foreach (SecuritySystemRole parentRole in securitySystemRole.ParentRoles)
        {
            CopyParentRole(parentRole, permissionPolicyRole);
        }
    }

    if (permissionPolicyUser != null && !permissionPolicyUser.Roles.Any(x => x.Name == permissionPolicyRole.Name))
    {
        permissionPolicyUser.Roles.Add(permissionPolicyRole);
    }

}
private void CopyParentRole(SecuritySystemRole parentRole, PermissionPolicyRole permissionPolicyRole)
{
    if (parentRole.IsAdministrative)
    {
        permissionPolicyRole.IsAdministrative = true;
    }

    if (parentRole.CanEditModel)
    {
        permissionPolicyRole.IsAdministrative = true;
    }
    foreach (SecuritySystemTypePermissionObject securitySystemTypePermissionObject in parentRole.TypePermissions)
    {
        CopyTypePermissions(securitySystemTypePermissionObject, parentRole, permissionPolicyRole);
    }
    foreach (SecuritySystemRole subParentRole in parentRole.ParentRoles)
    {
        CopyParentRole(subParentRole, permissionPolicyRole);
    }
}
private void CopyTypePermissions(SecuritySystemTypePermissionObject securitySystemTypePermissionObject, SecuritySystemRole securitySystemRole, PermissionPolicyRole permissionPolicyRole)
{
    PermissionPolicyTypePermissionObject permissionPolicyTypePermissionObject = ObjectSpace.FindObject<PermissionPolicyTypePermissionObject>(new BinaryOperator("TargetType", securitySystemTypePermissionObject.TargetType));
    permissionPolicyTypePermissionObject = ObjectSpace.CreateObject<PermissionPolicyTypePermissionObject>();
    permissionPolicyTypePermissionObject.TargetType = GetTargetType(securitySystemTypePermissionObject.TargetType);
    permissionPolicyTypePermissionObject.Role = permissionPolicyRole;
    if (securitySystemTypePermissionObject.AllowRead)
    {
        permissionPolicyTypePermissionObject.ReadState = SecurityPermissionState.Allow;
    }
    if (securitySystemTypePermissionObject.AllowWrite)
    {
        permissionPolicyTypePermissionObject.WriteState = SecurityPermissionState.Allow;
    }
    if (securitySystemTypePermissionObject.AllowCreate)
    {
        permissionPolicyTypePermissionObject.CreateState = SecurityPermissionState.Allow;
    }
    if (securitySystemTypePermissionObject.AllowDelete)
    {
        permissionPolicyTypePermissionObject.DeleteState = SecurityPermissionState.Allow;
    }
    if (securitySystemTypePermissionObject.AllowNavigate)
    {
        permissionPolicyTypePermissionObject.NavigateState = SecurityPermissionState.Allow;
    }
    foreach (SecuritySystemObjectPermissionsObject securitySystemObjectPermissionsObject in securitySystemTypePermissionObject.ObjectPermissions)
    {
        CopyObjectPermissions(securitySystemObjectPermissionsObject, permissionPolicyTypePermissionObject);
    }
    foreach (SecuritySystemMemberPermissionsObject securitySystemMemberPermissionsObject in securitySystemTypePermissionObject.MemberPermissions)
    {
        CopyMemberPermission(securitySystemMemberPermissionsObject, permissionPolicyTypePermissionObject);
    }
    permissionPolicyRole.TypePermissions.Add(permissionPolicyTypePermissionObject);
}
private void CopyMemberPermission(SecuritySystemMemberPermissionsObject securitySystemMemberPermissionsObject, PermissionPolicyTypePermissionObject permissionPolicyTypePermissionObject)
{
    PermissionPolicyMemberPermissionsObject permissionPolicyMemberPermissionsObject = ObjectSpace.CreateObject<PermissionPolicyMemberPermissionsObject>();
    permissionPolicyMemberPermissionsObject.TypePermissionObject = permissionPolicyTypePermissionObject;
    if (securitySystemMemberPermissionsObject.AllowRead)
    {
        permissionPolicyMemberPermissionsObject.ReadState = SecurityPermissionState.Allow;
    }
    if (securitySystemMemberPermissionsObject.AllowWrite)
    {
        permissionPolicyMemberPermissionsObject.WriteState = SecurityPermissionState.Allow;
    }
    permissionPolicyMemberPermissionsObject.Members = securitySystemMemberPermissionsObject.Members;
    permissionPolicyMemberPermissionsObject.Criteria = securitySystemMemberPermissionsObject.Criteria;
    permissionPolicyTypePermissionObject.MemberPermissions.Add(permissionPolicyMemberPermissionsObject);
}
private void CopyObjectPermissions(SecuritySystemObjectPermissionsObject securitySystemObjectPermissionsObject, PermissionPolicyTypePermissionObject permissionPolicyTypePermissionObject)
{
    PermissionPolicyObjectPermissionsObject permissionPolicyObjectPermissionsObject = ObjectSpace.CreateObject<PermissionPolicyObjectPermissionsObject>();
    permissionPolicyObjectPermissionsObject.TypePermissionObject = permissionPolicyTypePermissionObject;
    if (securitySystemObjectPermissionsObject.AllowRead)
    {
        permissionPolicyObjectPermissionsObject.ReadState = SecurityPermissionState.Allow;
    }
    if (securitySystemObjectPermissionsObject.AllowWrite)
    {
        permissionPolicyObjectPermissionsObject.WriteState = SecurityPermissionState.Allow;
    }
    if (securitySystemObjectPermissionsObject.AllowDelete)
    {
        permissionPolicyObjectPermissionsObject.DeleteState = SecurityPermissionState.Allow;
    }
    if (securitySystemObjectPermissionsObject.AllowNavigate)
    {
        permissionPolicyObjectPermissionsObject.NavigateState = SecurityPermissionState.Allow;
    }
    permissionPolicyObjectPermissionsObject.Criteria = securitySystemObjectPermissionsObject.Criteria;
    permissionPolicyTypePermissionObject.ObjectPermissions.Add(permissionPolicyObjectPermissionsObject);
}
private Type GetTargetType(Type currentType)
{
    if (currentType == null) return null;

    Type outType;
    if (!SecurityAssociationClassDictionary.TryGetValue(currentType, out outType))
    {
        outType = currentType;
    }
    return outType;
}
private static Dictionary<Type, Type> SecurityAssociationClassDictionary = new Dictionary<Type, Type>(){
    { typeof(SecuritySystemUser),typeof(PermissionPolicyUser) },
    { typeof(SecuritySystemRole),typeof(PermissionPolicyRole) },
    { typeof(SecuritySystemTypePermissionObject ),typeof(PermissionPolicyTypePermissionObject ) },
    { typeof(SecuritySystemObjectPermissionsObject ),typeof(PermissionPolicyObjectPermissionsObject ) },
    { typeof(SecuritySystemMemberPermissionsObject ),typeof(PermissionPolicyMemberPermissionsObject ) }
};

Gimmicks

I had rather strange situation where above code failed to load old SecuritySystemUser and SecuritySystemRole because system was switched to new permission policy classes, so it seemed old ones were no longer registered by XAF. 
Usually, I would fix that by locating those classes in agnostic module designer, find desired type and simply select Use in application. 
But for some reason I could not find those classes anywhere, so instead I put following code in Module.cs constructor:

C#
public LinkModule()

{

    AdditionalExportedTypes.AddRange(

                new Type[] { typeof(SecuritySystemUser), typeof(SecuritySystemRole) });

    InitializeComponent();

}

And this did the trick, old classes were populated correctly.

Finish line

And that's it :-)

Your scenario will surely be different than mine, but process is the same - actual SQL scripts will need to be adjusted to cover your spefics.
If some more details are required, don't be shy and drop me a word or two.

Good luck migrating!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)