Ticket T951640
Visible to All Users

Security - Best Practices for Export/Import Role Permissions at runtime (without releasing a new application version to clients)

created 4 years ago

I have the same task like this Ticket T604600
I have a development database where I create Roles for my application.
When I wish to export Role(s) to file and send it to Client.
New role is Adding, existing is to Update.

Some change? XAF have the some built-in function to export\import Role Permissions or need to create self?

Answers approved by DevExpress Support

created 4 years ago (modified 4 years ago)

Hello,

Yes, you can use the new solution: How to generate database updater code for security roles created via the application UI in a development environment. How would it work for you?

If this does not work for you, you can dump required data records from the development database to an XML file and then load this XML file from an application using the production database. Take special note that since the algorithm in the example below copies the full object graph, your development and production database must contain only unique records. For instance, if you save the dump for the selected security role, then its security user will be saved as well and this user must not exist in the production database.

C#
using DevExpress.ExpressApp; using DevExpress.ExpressApp.Actions; using DevExpress.ExpressApp.DC; using DevExpress.ExpressApp.Xpo; using DevExpress.Persistent.Base; using DevExpress.Persistent.BaseImpl; using DevExpress.Persistent.BaseImpl.PermissionPolicy; using DevExpress.Xpo; using DevExpress.Xpo.DB; using DevExpress.Xpo.Helpers; using System; using System.Collections; using System.Linq; using System.IO; using System.Xml; namespace MainDemo.Module.Controllers { public class ImportExportRoleViewController : ObjectViewController<ListView, PermissionPolicyRole> { public ImportExportRoleViewController() { #if DEBUG new SimpleAction(this, "ExportToXml", "View", (s, e) => { ExportToXml(e.SelectedObjects); }) { SelectionDependencyType = SelectionDependencyType.RequireMultipleObjects }; #endif new SimpleAction(this, "ImportFromXml", "View", (s, e) => { var os = Application.CreateObjectSpace(typeof(ImportRoleParameters)); var dv = Application.CreateDetailView(os, os.CreateObject<ImportRoleParameters>()); Application.ShowViewStrategy.ShowViewInPopupWindow(dv, okDelegate: () => { if(((ImportRoleParameters)dv.CurrentObject)?.File is var file && file != null) { ImportFromXMl(file); } }); }) { SelectionDependencyType = SelectionDependencyType.Independent }; } protected override void OnActivated() { base.OnActivated(); bool isAdmin = ((PermissionPolicyUser)SecuritySystem.CurrentUser).Roles.Any(r => r.IsAdministrative); Array.ForEach<ActionBase>(this.Actions.ToArray<ActionBase>(),(a) => a.Active["DisabledForNonAdmins"] = isAdmin); Application.ObjectSpaceCreated += Application_ObjectSpaceCreated; } private void Application_ObjectSpaceCreated(object sender, ObjectSpaceCreatedEventArgs e) { var nonPersistentObjectSpace = e.ObjectSpace as NonPersistentObjectSpace; if(nonPersistentObjectSpace != null) { IObjectSpace additionalObjectSpace = Application.CreateObjectSpace(typeof(FileData)); nonPersistentObjectSpace.AdditionalObjectSpaces.Add(additionalObjectSpace); nonPersistentObjectSpace.AutoDisposeAdditionalObjectSpaces = true; } } protected override void OnDeactivated() { Application.ObjectSpaceCreated -= Application_ObjectSpaceCreated; base.OnDeactivated(); } protected virtual void ExportToXml(IList objects) { var sourceSession = ((XPObjectSpace)View.ObjectSpace).Session; var dataStore = new InMemoryDataStore(AutoCreateOption.DatabaseAndSchema); var dataLayer = new SimpleDataLayer(sourceSession.Dictionary, dataStore); var destinationSession = new UnitOfWork(dataLayer); ClonerHelper.Clone(sourceSession, destinationSession, objects); destinationSession.CommitChanges(); // Dennis: This is supposed to be executed only in development environment in Debug mode where we have all file system permissions. We intentionally hide this Action in Release mode. dataStore.WriteXml("Roles_" + DateTime.Now.ToString("MM-dd-yy_H-mm-ss.xpoxmldump")); } protected virtual void ImportFromXMl(FileData file) { try { var destinationSession = (UnitOfWork)((XPObjectSpace)View.ObjectSpace).Session; var dataStore = new InMemoryDataStore(AutoCreateOption.DatabaseAndSchema); var dataLayer = new SimpleDataLayer(destinationSession.Dictionary, dataStore); using(var stream = new MemoryStream()) { file.SaveToStream(stream); stream.Position = 0; using(var reader = new XmlTextReader(stream)) { dataStore.ReadXml(reader); } } var sourceSession = new UnitOfWork(dataLayer); ClonerHelper.Clone(sourceSession, destinationSession, View.ObjectTypeInfo.Type); destinationSession.CommitChanges(); View.Refresh(true); } catch(Exception) { // Dennis: For instance, if you exported roles for a user Sam with a primary key X a development database, the production database must not have users, roles, permissions with the same primary keys. throw new UserFriendlyException("Make sure that your XML file with security data does not reference the same records as in production database."); } } } [XafDisplayName("Import Roles"), DomainComponent] public class ImportRoleParameters : NonPersistentBaseObject { private FileData _File; [FileTypeFilter("XPO Object Dump Files", "*.xpoxmldump")] [ToolTip("Specify an XML file with security data from your development environment.")] public FileData File { get { return _File; } set { SetPropertyValue<FileData>(ref _File, value); } } } }

Important Notes

If you have a custom security role and user classes (for instance, ExtendedSecurityRole and Employee as in these examples: one, two), you need to use your custom security role type instead of PermissionPolicyRole and PermissionPolicyUser above.

    Show previous comments (7)
    Dennis Garavsky (DevExpress) 4 years ago

      Great! I and other community members would appreciate it if you post your final controllers here. This could be of great help to other people. Thank you!

        Maybe it not optimal (and not good style) code but it work…

        C#
        using DevExpress.ExpressApp; using DevExpress.ExpressApp.Actions; using DevExpress.ExpressApp.DC; using DevExpress.ExpressApp.Xpo; using DevExpress.Persistent.Base; using DevExpress.Persistent.BaseImpl; using DevExpress.Persistent.BaseImpl.PermissionPolicy; using DevExpress.Xpo; using DevExpress.Xpo.DB; using DevExpress.Xpo.Helpers; using System; using System.Collections; using System.Linq; using System.IO; using System.Xml; using DevExpress.Data.Filtering; namespace YourSolutionName.Module.Controllers { public partial class ImportController : ViewController { private IObjectSpace iosPolicyRole; private static Session sessionPolicyRole; //... private void ActionImport_Execute(object sender, PopupWindowShowActionExecuteEventArgs e) { iosPolicyRole = Application.CreateObjectSpace(typeof(PermissionPolicyRole)); sessionPolicyRole = ((XPObjectSpace)iosPolicyRole).Session; //... } public static void Import_PermissionPolicyRoleBase(string _FileName) { XmlDocument doc = new XmlDocument(); doc.Load(_FileName); Guid guidrole = Guid.Empty; if(doc.SelectSingleNode("DefaultName/PermissionPolicyRole/Oid") != null) if(!string.IsNullOrEmpty(doc.SelectSingleNode("DefaultName/PermissionPolicyRole/Oid").InnerText)) guidrole = Guid.Parse(doc.SelectSingleNode("DefaultName/PermissionPolicyRole/Oid").InnerText); string namerole = doc.SelectSingleNode("DefaultName/PermissionPolicyRole/Name")?.InnerText ?? ""; if(!Guid.Equals(guidrole, Guid.Empty) && !string.IsNullOrEmpty(namerole)) { PermissionPolicyRoleBase findrolebase = sessionPolicyRole.FindObject<PermissionPolicyRoleBase>(CriteriaOperator.Parse("Oid == ?", guidrole), selectDeleted: true); if(findrolebase == null) findrolebase = sessionPolicyRole.FindObject<PermissionPolicyRoleBase>(CriteriaOperator.Parse("Name == ?", namerole)); PermissionPolicyRole findrole = sessionPolicyRole.FindObject<PermissionPolicyRole>(CriteriaOperator.Parse("Oid == ?", guidrole), selectDeleted: true); if(findrole == null) findrole = sessionPolicyRole.FindObject<PermissionPolicyRole>(CriteriaOperator.Parse("Name == ?", namerole)); if(findrolebase == null && findrole == null) { try { var destinationSession = (UnitOfWork)sessionPolicyRole; var dataStore = new InMemoryDataStore(AutoCreateOption.DatabaseAndSchema); var dataLayer = new SimpleDataLayer(destinationSession.Dictionary, dataStore); dataStore.ReadXml(_FileName); var sourceSession = new UnitOfWork(dataLayer); ClonerHelper.Clone(sourceSession, destinationSession, typeof(PermissionPolicyRole)); destinationSession.CommitChanges(); sourceSession.CommitChanges(); } catch(Exception ex) { } } } } } }
        Dennis Garavsky (DevExpress) 4 years ago

          Cool - thanks for sharing! I've just included "using" statements into your code above to save time for future explorers:-)

          Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

          Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.