Implementing StrataFrame Security
Strataframe has everything you need to build a world-class security system for your application
Coding time: 20 minutes.
Although many applications are designed to expose all of their functionality to all users, there are cases where not everyone can do everything that the app does. Typically, this is the case when you want to deliver one application to a range of user types, and thus maintain just one code base. Administrators or managers need access to at least everything in their department; workers may need to see only the tasks they're assigned to. I personally once accidentally picked up a report of the salaries of the executives at the company where I was working, and I almost quit that very day. So information control is often very, very important.
Whatever your reason, you'll be happy to know that StrataFrame comes with a robust security system that can easily be incorporated into your application. It lets you start with a StrataFrame login form to provide a gate into your app, and then provides you with another form to let administrators manage the security system. You then add function calls to the security tables at various places inside your forms. In fact, some security functionality is handled automatically by StrataFrame properties, and requires no code at all.
If all you want to do is provide a login for authorized users, StrataFrame can do that, too, and the entire process will take you about two minutes. But once you see how easy it is to craft a sophisticated control mechanism, your users will want you to take full advantage of it. You're being paid by the hour, right?
In the StrataFrame menu, you'll see Role-Based Security Editor; this is the starting point for role-based security in Strataframe. The Security Editor creates a Security Project where your security information is stored. You then deploy this information to a SQL Server database (which can be the same one where your application's tables live). This is done in three easy steps:
- Use the Security Editor to create permissions that are meaningful to your application, storing them in a local Security Project cache;
- Use the Database Migration Wizard to create a SQL Server database for your project, and then add the data from the Security Project; and
- Add code in your application to use the security system tables added to SQL Server in step 2, and set properties that interact with the internal security methods of StrataFrame objects.
Permissions, Restrictions and Roles, Oh, my!
<rant>See, there was this movie called the Wizard of Oz, and there were lions and tigers and bears, and ... oh, you young folks don't know any of my classical references.</rant>
Security in StrataFrame applications is managed by means of users, roles, permissions and restrictions. Users can have roles, and roles have permissions and restrictions. Users can also override the specifics of a role by having additional permissions and/or restrictions; or, they can have permissions assigned without any role. It's completely flexible, and controlled by administrators and users who have permission to view the Security Editor, which you launch from your app's menu.
Permissions are the bread and butter of the StrataFrame security system. Roles are nothing but collections of permissions and restrictions. Generally, permissions will target either forms or fields. Either can generally be handled with a single line of code, and sometimes just by setting a property.
A permission is a character string, like "Can edit customers" or "Work weekends". A StrataFrame function called GetPermission(string) looks for a matching string in the security tables (SFPermissions, mainly) and return a valued of Granted or Denied. There are also events that look for a permission string stored in a property, like the ViewSecurityKey which StrataFrame forms compare to the current user's permissions before instantiating a form so that the user can View it. And there is a place in StrataFrame business objects where you can specify the security key string that will be checked when data is displayed to determine whether the current user can even see the data. You may be able to implement many of your security requirements without writing a single line of code!
Permissions aren't added by users
Note that permissions aren't added by users; permissions are created in the Security Editor that developers see in the StrataFrame menu pad in Visual Studio. This makes sense: Your code will refer to strings corresponding to the permissions that the developer must know before writing the code to access them. Allowing users to add codes that the program doesn't know about wouldn't do much good. So you'll create permissions in the IDE, and then migrate them to your SQL tables. You can then add roles and restrictions, and users can be added and assigned roles, permissions and restrictions as needed.
Since they're addressed in code, you design your security strategy by deciding which permissions you need. Any administrator, or anyone else having access to the Security Editor, can then assign them to roles (Administrators bypass the security system completely, so they always have access to all menu items.). A role can then be assigned to a users, instantly conferring all of the role's permissions to the user. Permissions can also be assigned directly to users. So, you'll add a Security Editor menu pad to your main menu, so that administrators and other authorized users can add users, roles or restrictions. At the end of this article, you'll learn how to code the Security Editor menu pad's Click event handler.
Setting up the project
First, you'll need to set up the project. Open Visual Studio and select New, StrataFrame Windows Application w/Security (Fig. 1). You'll have to choose the language, but in this article we'll include the code for both C# and VB. Name the project SFSecurity.
The project is created with a startup form named by default Form1. In the Solution Explorer window, change the name of Form1 to MainForm, allowing the IDE to change all references to the name Form1 as well (that includes the reference to it in program.cs). You can also change the form's Text property (the form's caption) to "Main Form" as I did. Add a MenuStrip, and after the obligatory File, Exit entries, add a second menu pad Tables with a single Customers submenu item, and another menu pad captioned Security Editor (Fig. 2).
A login form will be displayed at program startup, thanks to code that's automatically added to program.cs|AppMain.vb when a StrataFrame Security project is first created. If you want to look ahead, it's in Listing 1, below (you may have to click the Toggle Code View button to expand the program code listing). You will have to make two tiny changes to this code; we'll get to that later.
In order to provide an initial administrative login, three lines of code are automatically added in the InitApplication method of program.cs|AppMain.vb:
SecurityBasics.AdministratorUserName = "Administrator";
SecurityBasics.AdministratorPassword = "admin" + DateTime.Now.Day.ToString();
SecurityBasics.AdministratorUserPk = -1;
The password changes every day; it's is "admin" plus the numeric value of the current date's day: admin1, admin2, etc. This is your back door to get into the app if some fool deletes all of the users. You might not want to tell your users about it...
Your next step will be to create some permissions for your application in the StrataFrame Security Editor, and then deploy them to a SQL Server database. We'll start with a small collection of permissions, but your application may require many, many more.
The Role-Based Security Editor
Security is based on the permissions stored in tables in a SQL database and accessed by your application. You'll use the Security Editor to build an initial set of permissions, then use the Database Migration Wizard to create a SQL database and load the data from the Security Editor into its tables. Finally, you'll add a StrataFrame SecurityDialog to your application. The form that it displays uses the Security System tables in your SQL database to allow administrators, and users with permissions to do so, to manage your security strategy.
From the StrataFrame menu pad, select Role-Based Security Editor. Click the New button and supply a project name and description and a security key, as shown in Fig. 3. This key will be unique to the project. Be sure to write it down; you'll need it later.
The Security Project screen that next appears is where you create and manage permissions within the IDE. You'll create permissions, and then optionally assign them to roles and/or users. You can also assign roles to users. Administrators of your application can also do all of these things except create permissions, as we'll see below.
Adding the components of your security database
Fig. 5 shows the Security Editor Global Preferences screen. This is where you specify password lengths, how many login tries a user can have, what happens if they try unsuccessfully more than the allotted number of times, and a few other features that you've seen in just about every robust password scheme, including Windows Server.
Among the many things you can specify is the Complex Password requirement. If you leave this checked (the default), your user password have to conform to the following specification:
- The password must be at least six characters long.
- The password must contain characters from at least three of the following five categories:
- English uppercase characters (A -> Z)
- English lowercase characters (a -> z)
- Base 10 digits (0 -> 9)
- Non-alphanumeric or symbols (for example: !,$,#, or %)
- Unicode characters
- The password cannot contain three or more consecutive characters from a word in the user's account name. For example, if the account name is "John L. Doe", a password would not meet the minimum complexity requirements if any of the following combinations was contained within the password: "joh", "ohn", "doe".
<rant>Database administrators will love this, but if you're not anal retentive, you might want to uncheck this one.</rant>
The Users page
Fig. 6 shows the users page:
To add a user, highlight the users node and click on the Add User menu button at the top of the form. You'll see the Add User dialog shown in Fig. 7:
I've checked Administrator, which means that the entire security system is ignored for user Les. This is so that you can get into the program and have full access to the Security Editor as well as everything else. Be aware that if all of your users are administrators, you can't test your security system, because if the current user is an administrator, all calls to GetPermission() return True.
There are tabs for Roles and Permissions, but we don't have any roles or permissions yet, so that will have to wait. Add yourself as a user, and check Password Never Expires. Be sure to write down your password. If you didn't uncheck Complex password in the Global Preferences page, you'll know why right about now...
<rant>NOTE: You don't have to add a user. As mentioned above, the InitApplication routine in program.cs contains 3 lines of code that adds an Administrator with a password consisting of "admin" plus the numeric value of today's date, (e.g. admin24); This will get you sufficient rights to log into the application, add users and roles, and assign them permissions. But you may have experienced Vista, which periodically told me I didn't have the right to run software on my own &*()^%$ computer, and decided that you're not taking any crap from a computer ever again. So if you add yourself, be sure to make yourself an administrator.</rant>
The Permissions page
The next thing you'll want to do is to add permissions. We're skipping over roles because roles are just collections of permissions; so, until you have some permissions, there's nothing to do in the roles section.
Fig. 8 shows the permissions page.
In Fig. 9 I've added a permission called View Customers, in a new category called View You can use Categories to group permissions in meaningful ways. Neither permissions nor categories have any intrinsic meaning; permissions are just strings that you can look for in association with the active user, and categories exist in the Security Editor only to help keep you organized.
In Fig. 10 I'm adding "View Emails" in the View category, specifying Replace Each Character as the Action. This will cause the screen to display x's in place of the email address in a textbox. Note that the textbox has no clue that it contains an email address; I just open the Business Object Mapper and associate the CustomersBO.Email SecurityKey property with "View Emails", and StrataFrame objects' internal methods do the rest.
Finally, I added a permission called "Application Security". This is the permission that I'll test for to determine who (other than administrators) has access to the Security Editor. It's not built into StrataFrame; it's simply a character string that the GetPermission() method will look in association with the current user (providing the current user is not an administrator), and return either Grant or Deny to let my code decide whether to enable or disable the corresponding menu pad.
The Restriction Sets page
Restriction Sets give you a tool for restricting rights based on times and workstations. You can define working hours and which workstation(s) a user can use.
Fig. 11.shows the Restriction Sets page.
To add a new Restriction Set, highlight the Restrictions Sets node and click on the Add Restriction Sets menu button; Fig. 12 will appear:
As can be seen above, restriction sets control days and times that users can use the system. This means, among other things, that unauthorized login attempts outside of work hours can be tracked automatically. That should keep the eager beavers in their place. Anyway, for now we don't need to add restrictions. They can be added by anyone running the application who is either an administrator or has the "Application Security" permission assigned to them, either directly or via their role.
Now you're ready to see how roles work.
The Roles page
Fig. 13 shows the roles page.
Roles include major functional categories, and provide a quick way to characterize the access rights of groups of users, without having to specify them individually. Roles are simply containers for permissions; that's why we had to add them first.
To add a new role, highlight the roles node and click on the Add Role menu button (or you can right-click on the roles node and select New Role from the context menu); Fig. 14 will appear:
I've added two roles, Accountants and Clerks. I'll use these later in this example. For now, just think of them as strings stored in a little database under the "Roles" heading. We can assign permissions to roles, and roles to users, while running the application.
Assigning Permissions to a role
Highlight the Accountants node and click on the Edit Selected Role button at the top of the screen. Notice that there's a Permissions tab. Select it, and you'll see the screen shown in Fig. 15, below. Check the first (Permissions) checkbox to indicate that all permissions are granted, and click Save to close the dialog.
Clerks will have the right to view customers, but not emails (Fig. 16):
Clearly, you can sculpt the user's rights to a very fine degree. If you add a user and assign them to either of the roles, you'll see that the corresponding permissions are automatically assigned. By just clicking through a few screens and adding users, roles and permissions, then assigning users to roles and perhaps creating a few user overrides, we can create a security strategy for our application.But how to we use this information?
The Database Deployment Toolkit
Creating the Security Database
So now you've got a user and some permissions and roles. They're stored in a local cache, in encrypted XML. This security project data has to be moved to SQL Server tables. After all, your users might be anywhere that they can get a connection to SQL Server, so the security data has to live in the same place.
Fortunately, the fine folks at StrataFrame have built a tool for moving data from anywhere into SQL Server tables. This tool, called the Database Deployment Toolkit or DDT, can be used to move the collection of users, roles and permissions that you just built into the database that also houses your data. However, you might prefer to have two databases, one for security and one for the application data. That's the model we'll use in this article. (If you add the StrataFrame security tables to your database, you can leave the three lines of code that add the security database out of program.cs|AppMain.vb.)
Although your data is in an encrypted XML cache, the twelve tables that will form the heart of your security system are in the StrataFrame (not the StrataFrameSample) database. In this step, you'll create a new database containing empty copies of these 12 tables, and then merge the data from your Security Project into the tables.
From the StrataFrame menu pad, open the Database Deployment Toolkit, which is shown in Fig. 17, and provide a name and location for the package that we're about to build. This is like a SQL script: it tells the Migration Wizard what to do.
You also have to tell the Database Deployment Toolkit the name of the database where you're going to create your security tables. If you don't already have an empty security database, create one called Security12 (that's the name used in this article - it doesn't really matter.) You can click the fourth button from the left in the Database Deployment Toolkit toolbar to create this database. When it's been created, you can go on to the next step.
Select the node that you just created, highlight the Deployment Data node and click the Deployment Data Wizard icon (the seventh one over - it's sort of golden yellow). The wizard will guide you through the process of selecting the StrataFrame security table model and populating it with your data. Click Next to get on with the process.
The first step in the Wizard is to select the Data Type. The purpose of this Wizard is to copy the security tables from the StrataFrame (not the StrataFrameSample) database and add them to our new database. So, select the Role Based Security radiobutton, as shown in Fig. 19:
In Fig. 20, you select the StrataFrame database as the Source database:
Next, select the destination database. This will hold the security system tables. Ours is named SFArticle12:
The Project Selection step is where you check the name of your Security project. Ours was SFSecurity, so check it and click Next:
Fig. 23 will appear in a few seconds, indicating that the data migration package, a file with the extension .pkg, has been created.
Now you're ready to load your security project data; select Deployment Data, then click the Deploy To Server icon (Fig. 24).
After a confirming dialog that asks if you want to create a new deployment package (click "Yes"), you'll see Fig. 25, indicating that the package has been created. Click Ok to allow it to install. In the next dialog screen (Fig. 26), Standard Deployment will be selected; click Next to continue. You'll click Next another four or five times before you see Fig. 27, indicating that deployment of your Security project (users, roles and permissions) to the SFArticle1 database has finished.
If you open the SQL Server Management Studio, you'll find your new database with the eight SF Security tables. You can browse through them to see the users, roles and permissions previously created in the Security editor.
Your users can now work directly with the security tables in the SFArticle1 database to add roles and users, and to assign permissions to either. We'll add a menu option shortly that lets them do so. Permissions, however, can only be added by the developer, since we refer to specific permissions in code to determine who does what. To add permissions, you (the developer) will run the Security editor within the IDE, then re-run this deployment package. It will merge your new and changed permissions with the existing records, so that records added by the users are not disturbed. The merge process is very smart, so it's not going to lose additions or changes made by your users.
We've now created a group of tables containing permissions, a couple of roles, and a single user (Les/sample). It took a while to describe, but it only takes a couple of minutes to actually do what was described above. Now, we'll see how you use these permissions in your code.
Using StrataFrame Security
Now that you've built the initial cache of permissions, and perhaps a user or two and a role or two, you need to tell your application where they are and what they're for. The code may look a little unfamiliar, but you only have to change two or three strings, and optionally add one line of code. In fact, I've removed the comments from the generated code in program.cs|AppMain.vb to emphasize how little code is required; you may want to create an app and read the comments to supplement your understanding of StrataFrame's inner workings.
Using the security database in your application
The entry point for your application, generated automatically when you create a StrataFrame Security project, is called program.cs (or AppMain.vb). It needs a little attention.
When the program first runs, your users will be prompted twice for a server and database name. The first prompt refers to your "data" database; the second refers to the Security database. The default names that are passed as the third parameter in the call to AddRequiredDataSourceItem are the default database names that they'll be asked to accept. (Note that they can enter any database name, as long as it contains the tables the app is looking for.) Note that the security tables can be added to your production database, in which case only one call to AddRequiredDataSourceItem() is required. Once they've supplied these names, the application remembers them, together with the connection strings built when they were first selected.
If you pick the wrong database and want to change it later, you have to either delete the cache (located on my Windows 7 machine at C:\Users\Les\AppData\Local\Temp\MicroFour\Metadata), or change the value of ConnectionManager.ApplicationKey at the start of the SetDataSources method in program.cs|AppMain.vb and re-run the application.
In Listing 1, I've used the first AddRequiredDataSourceItem() call to register the ConnectionString for your application's main database; by default, it's a blank key (the first parameter). The second call to AddRequiredDataSourceItem() is to register the ConnectionString for the Security database. The first parameter in this second call (the DataSourceKey for the security database connectionString) must be assigned to SecurityBasics.SecurityDataSourceKey at the end of SetDataSources(), to tell it which of the two database connections to use for Security. Simple as that..
Security adds one additional requirement: In the InitApplication() method a few lines below, you need to supply the string that you entered as the Security Database Security Key, which was "SFS" (see Fig. 3), as the only parameter in the call to SecurityBasics.SetSecurityKeyAndVectorForUserAuthentication. The line to change is highlighted in yellow in Listing 1. This is the encryption key for the security data.
The first thing you'll need to do is inform your application about the existence of this little database. To do that, select Solution Preferences from the StrataFrame menu and fill in the dialog that appears, as shown in Fig. 28:
In addition, in program.cs|AppMain.vb you'll have to inform your application about both the location of your SQL database and the security code for your security system tables:
So how do you use this security system? Much of it is done in code. If you want to disable or hide controls, use SecurityBasics.CurrentUser.GetPermission(PermissionString to disable or hide buttons. Put the code in the form's constructor, just after InitializeComponent.
You use the Action property of the SecurityBasics.CurrentUser.GetPermission() method to return the PermissionAction of a particular Security Key, then compare the returned value to the appropriate PermissionAction. You can do this to set the Enabled or Visible property of a button, a menuitem, or anything else. For example, to hide a phone number, add a "View Phones" permission, then add this code in the form's InitApplication code:
txtPhone.Visible = SecurityBasics.CurrentUser.GetPermission("View Phones").Action == PermissionAction.Grant;
If a user doesn't have that particular permission, either assigned directly or via a role, the field will be hidden.
Exposing the Security Editor
There is, however, a little code that you'll want to add to your Security Editor menu pad. Administrators and perhaps others should be able to add roles and users and to assign them permissions, so we'll need to include the Security Editor in the application.
Using GetPermission() in code
The most basic programmatic control is done using the GetPermission() function. To prevent instantiation of a form, you compare the return value of
to the enums PermissionAction.Grant or PermissionAction.Deny.
To limit access to, say, the Customers form Add button, add a "Customer Add" permission in the Security Editor, assign it to those of your users who can add customers, then include this in the form's constructor:
C#: btnEdit.Enabled = SecurityBasics.CurrentUser.GetPermission("Customer Add").Action == PermissionAction.Grant;
VB: btnEdit.Enabled = SecurityBasics.CurrentUser.GetPermission("Customer Add").Action == PermissionAction.Grant
In our sample application (Fig. 2, above), we can launch the Security Editor, and we can display the Customer Maintenance form, but only if security permits the current user to do so. Listing 2 shows the complete code for MainForm, which includes the code to respond to the securityEditorToolStripMenuItem Click event. I've added "Application Security" to the Permissions list, so that the test for GetPermission("Application Security") is looking for that exact security key in the Permissions table, and checking whether the current user has that specific permission, either directly assigned, or through a role assignment.
Note that testing for SecurityBasics.CurrentUser.IsAdministrator isn't necessary, because administrators have all rights, i.e. GetPermission("whatever") always returns True.
Security Without Code
There are a number of security features build into StrataFrame that require no coding whatsoever. These features, plus the login to screen unauthorized users, may be all you need to meet your security strategy needs.
In order to test the code, I added a SF Maintenance form called frmCustomers to the application, dropped a CustomersBO object on it, added five labels and textboxes, and bound the textboxes to five of the columns in the Customers table. I then added a single line of code in the Load event of the form:
The screen appears in Fig. 29, below.
We have a Security Key with the value "View Customers", Instead of writing code to test for the presence of this key, you can simply find the form's ViewSecurityKey (which means the security key that controls viewing the form) and enter "View Customers". Since we already tested the GetPermission function used above to suppress the Customer form display, comment out the code that invoked it, add the property setting shown in Fig. 29, and run the form again, and you'll get the "Access Denied" dialog. Pretty slick for no code, eh?
But wait, there's more! Remember that restriction on Clerks, who can't see emails? According to our permission, if permission is denied, each character of the actual email will be replaced with a lower-case "x". In Fig. 30, I've selected the "View Emails" security key for the cust_Email column in the Customers business object (double-click on the cust_Email row to open the dialog). Fig. 31 shows that if the user is assigned the role of a Clerk, and Clerks can't , the contents of the cust_Email field is replaced with x's.
I leave it to you as an exercise to add permissions, reference them in your code, and see how they work. Once you have the basic mechanism, it's pretty straightforward. But you may be able to do a lot with no code at all.
Security is an important part of many business applications. StrataFrame provides the framework (hence the name) for a robust security system; you just plug it in and use it. Grab a cup of coffee and walk through this exercise; it will take you about 20 minutes, and you'll be amazed at what you can do with just a little code.
If you'd like to read more about StrataFrame security before you dive in, there's a high-level discussion of StrataFrame security at StrataFrame.Net/RoleBasedSecurity.aspx.
|Copyright(C) Pinter Consulting, 2018||Tel: +1 (650) 464-6924|