Different users do different things. Cashiers serve customers and register Sales documents. My part-time accountant David Brent registers business transactions and calculates wages.
The small problem here is that they all use the same interface and therefore see many things they don’t need. The bigger problem is that they can see things they are not supposed to. For example, I’m not sure it’s such a great idea to let Daniel see all Agnes’s earnings and vice versa.
So, we need to grant or deny access to any piece of information depending on the user’s role. This is what this episode is about.
Two entities this entire functionality is revolving around are Roles and Users. Roles are nothing else but named sets of access rights, while users are the people working with the app. For example, we can create a Role named Cashiers and add all access rights to work with the Sales documents to this role. Then we can bind this role to the Agnes Blue user and Agnes will be granted full access to Sales documents right away.
There is a many-to-many relation between Roles and Users. Meaning that one role can be assigned to any number of users, as well as any user can belong to any number of roles.
For now there are no Roles and no Users in the application. With this kind of a setup the Platform does not require authentication and does not restrict any access. So anybody can run the application and do whatever they like.
But as soon as I add the first user, everything changes. Right after I click this OK button, there will be no access to anything whatsoever unless it’s granted explicitly. So, the Platform doesn’t even let me save this user because if it does, I will lose access to the Designer and won’t have enough rights to ever fix this.
To work with the Designer I need administrative rights. So, before adding any users I need to create at least one role which I’m gonna call Admin, and grant it the administrative rights. Then I need to save the application, and only now I can add my first user.
And this is where we bind roles and users together. So, I’m including this user in the Admin role and now I can save it without any error messages. From now on there will be no free application entry anymore.
Now, If I need to run the app I must identify myself by selecting my login name from this list and specifying a correct password, that can be set here in the Designer or by using this write-only property of InfoBaseUser class from anywhere in your source code.
We can also map operating system users to our application users, in which case there will be no password asked during login to the app. We can also leave the password empty, which counts as a legitimate password, so if I try to enter anything here, nobody will let me in.
Although, entering a correct password is a necessary condition for entering the app but not always a sufficient one. In this specific case the role my user belongs to does not have access to this
CompanyName constant that gets read at the system startup and blocks me from getting in.
So, let’s get back to the role and see what access rights are here for us to play with. The left frame of this form is for the subset metadata objects that access rights make sense for. The right upper frame is for the access rights available for the selected object in the tree. At the top level of the tree we have everything to do with the application as a whole. This is our administrative right, rights to run different types of clients and stuff like that. Everything is pretty much self-explanatory.
Some of the rights depend on each other. For example we can do data administration without general administrative rights but not the other way around. So, when I uncheck this Data administration rights and then check the general Administration rights, this box gets checked by the Platform automatically.
One level down the tree is where Metadata Classes access rights live. Of course, these rights have nothing to do with metadata objects editing, which is regulated by this Administrative right up here. These metadata-classes-level checkboxes are just shortcuts helping us to set specific rights to all metadata objects of this class. And, of course, the metadata-objects-level rights are fully independent and can be switched off or on for each individual object.
OK, let’s look at the constant that we have no access to and therefore cannot run the app because of it. I don’t know about you but I’m a bit confused about these four guys over here. What exactly is the difference between Read and View or between Update and Edit?
OK, let’s just check the Read right and see if it’s enough to enter the app. And it is indeed! And this is my CompanyName constant successfully read from the source code and shown in the form title. How about the View right then? What does it regulate? Let me show you. This is my application tree and this is where my constant is supposed to be. But now it looks like I have no constants at all. Let’s check the View right like this, and here we go. This is my constant in the application tree.
I can see it, I can open its form but I cannot change it in any way. Which is apparently regulated by these Update and Edit guys. If I grant my role an Update right and then write a piece of code changing the CompanyName constant value, I will be able to run the command and change the constant like this but still won’t have any luck editing it from the interface. But as soon as I grant myself this Edit right the CompanyName is perfectly editable.
So, to sum this up. To read the data from the database you need to have the Read right. And to write it back you need the Update right. These are what we call data access rights. They allow the Platform and the source code to manipulate the data. But for users to see and edit it, they need to have so-called interactive rights: the View and the Edit right in this case.
Other metadata classes have very similar sets of data access and interactive rights with some class-specific additions like this Insert and Delete rights you’re gonna need to work with a list rather than with a single value.
One interactive right deserving special attention is called the Interactive Delete right. Let me show real quick what it is about. I’m adding these two new catalogs and referring from the second to the first one like this. Then, I’m giving my role the full access to both catalogs except for the interactive deletion for the first one.
Running the app. Adding an item to the first catalog and referring to this item from the second catalog like this.
Now. What if I want to delete this first item? Well, my only option is to mark it for deletion like this, and then run this standard procedure, that will tell me that the Item cannot be deleted because there is another data object that uses this item reference.
But if there is the interactive deletion right, nobody will prevent me from deleting this item and leaving an orphan reference sitting here in Catalog 2 and making no sense at all. So this interactive deletion access right regulates who is in charge of data referential integrity - the requirement for each reference in the system to point at an existing object. If the access is denied, the Platform takes the full care of referential integrity, so nobody else has to. If the right is granted, the Platform steps down and either your user or your source code has to take over this responsibility. Unless you’re okay with orphan references, of course.
Further down the metadata tree live field-level rights, so you can grant or deny access to specific attributes if need be. The same applies to the tabular sections and their attributes. Now. What happens if a user belongs to multiple roles with contradicting rights? This is what.
If any of the Roles has the permission, the user gets it too. If none of the Roles has it, the user doesn’t get it. As important as the access control is, sometimes we might want to switch it off completely for some operations. Here is an example for you. If I have a right to create, edit and post a Sales document, I know everything about its content.
Whichever registers the document posts its records to, I already saw all the data, and it doesn’t make much sense to spend resources checking if I have a right to write them to the registers. What does make sense here, is to tell the Platform to temporarily ignore all access restrictions. This is done by switching on so-called privileged mode.
We use the privileged mode to speed things up by not checking the access rights when we don’t need it. The privileged mode works only on the server side and is turned on or off by the application settings or the source code. It means that a user cannot control it and, therefore, doesn’t need any special rights for this mode.
This is when we want to use the privileged mode:
- When a user needs to run some code accessing data a user has no access to.
- When you need to speed up a batch processing of a big chunk of data.
- When you need to perform administrative tasks disregarding any restrictions.
When it comes to documents, posting to registers in the privileged mode is so common, that there are settings doing exactly this. These flags are on by default, and we usually don’t set access rights for registers with the possible exception of the Read right for users who run reports on some specific registers.
Another common case is when we need the access rights disabled from the source code, which is done by using this SetPrivilegedMode procedure of the Global Context. For example, I can switch off this Sales document flag, then go to the Posting event handler and call SetPrivilegedMode here at the top. In this case the document behaves as if the flag is still on: user’s access rights are ignored and this piece of code can work with any data it likes.
This do-all-you-want policy stays in effect until the end of the procedure or until I switch it off explicitly, like this.
Another way of making your code immune to access restrictions is to place it inside of a module with this flag turned on. Any procedure written in such a module will be run in the privileged mode.
One problem with the privileged mode is that it can be exploited as a backdoor to access restricted data. Here is, for example, the newest and coolest payroll feature, I implemented recently for my part-time payroll accountant David Brent.
I added this new text field to the EarningTypes chart for a user to be able to write his custom bonus calculation formulas instead of hard-coding them into the application. This Sales Bonus earning, for example, calculates bonus as a percentage of sales over a period of time.
So, now I can add whatever custom-build formulas I need and then use them to calculate bonuses for my cashiers, like this. But here is the catch. As you can see, David is not supposed to have access to anything but the Payroll subsystem. What he does have access to is this formula text, and if he feels a little sneaky today, he can just replace this formula with something like this.
And then… Wait a second. Is this… Yes, it’s all the accounting entries we’ve got in the system.
How did this happen? This is how. This is the text sneaky David wrote. And this is how the bonus gets calculated: the custom formula text gets passed to the Execute procedure, which literally embeds this exact text right here in the source code during the run-time.
And if the first thing I do is turning the privileged mode on, I get unrestricted access to any data regardless of my roles. This is why every time we do something potentially dangerous like executing custom code, we need to use the privileged mode antidote called the safe mode, which tells the Platform to ignore the privileged mode and keep checking the access rights.
And this is how it’s done. I’m turning the safe mode before the Execute procedure call and turning it off after. And if David tries to pull off his little trick now the access violation error is all he gets.