Read the previous installments in the series: Getting started with the OutSystems Agile Platform, Learning the basics of the OutSystems Agile Platform, Describing the OutSystems Agile Platform Service Studio experience, Working with the OutSystems Agile Platform's Integration Studio, Deploying an application created with the OutSystems Agile Platform, Adding ECT to an eSpace with the OutSystems Agile Platform, Working with scheduled jobs in the OutSystems Agile Platform, and Using AJAX in the OutSystems Agile Platform.
Security is one of the most important features in any application; unfortunately, in software development, programmers often are not given the time (and they frequently lack the knowledge) to get the security angle right. When developers are facing a tight deadline, security (as well as QA and documentation) usually gets left behind.
The OutSystems Agile Platform has several features that make it very easy to bake security into your application, typically with very little effort. Here's a look at three types of security that I have worked with in the Agile Platform: authentication ("who are you?"), authorization ("are you allowed to do that?"), and encryption. I will also briefly cover the Enterprise Manager component.
Authentication is a core part of the Agile Platform. Every eSpace that is made has a User entity built into it. The User entity handles the basics: name, username, email address, password, and a couple of other fields. The User entity is locked down so you cannot modify it. In Rat Catcher, I made a USER_DETAIL entity to hold additional information, and I tied it to the User entity.
There are a number of Actions, functions, and other tools built into the system that allow you to work with users. For example, you can get the current user's information with the UserId and UserName Session Variables. You can check these against the NullIdentifier value to verify that the user is logged in.
If you need to see if a user is who they say they are and log them in, this is a cinch. If you have a guarantee that the person using the application is the right person but you do not have their password (such as someone retrieving their password or creating a new account), you can call the Login Action and pass it the UserId. But if you want to make sure that they have the right password, there is the LoginPassword Action.Note: The system does not automatically encrypt the passwords stored in the User entity. If you want those passwords to be encrypted, then when you create a new User entity, put the password through the Encrypt function first. That said, when you log the users in, there is no need to encrypt the password that they provide. You can also create random passwords with the GeneratePassword() function; I use this when the account administrators create new user accounts, so the new users get a random password that the account administrator does not know. If the LoginPassword fails, it throws out the Invalid Login error, which you can handle in your workflow and take appropriate action. Login and LoginPassword can be set to be "persistent" if you want. You can log users out with the Logout Action. You can see in Figure A how I wired together a login system for Rat Catcher. Figure A
A simple login Screen Action
To create users, you make a new record in the User entity — but remember to Encrypt() the password attribute first.
The Agile Platform allows you to create different roles, which are known as Permission Areas. Each user can have multiple Permission Areas assigned to them; in addition, there are two built-in Permission Areas: Anonymous and Registered. All users automatically have the Registered Permission Area, and anyone who is not logged in is a member of Anonymous. You create these Permission Areas in the eSpace tree by right-clicking Permission Areas and choosing Add Permission Area.
In Rat Catcher, I added the AccountAdministrator and GlobalAdministrator Permission Areas. Rat Catcher allows multiple users to be "owned" by an account, but only the one user with the AccountAdministrator permission may edit the other users or make decisions that affect the account. The other new Permission Area is reserved for people on my end who can edit anything in the system.
It's really easy to use the Permission Areas. If you want to restrict a Web Screen to certain users, select the Web Screen in the eSpace tree and, in the Permissions group of the Properties, select which Permission Areas are allowed to access the Web Screen. Whenever a user is referred to a Web Screen for which they do not have access, they receive an error. So, how do you keep users from getting to those screens?
In my Header Web Block, I use the If Widget only to show navigation to areas to which the user can access. In the Expression editor, you can use the CheckXYZPermission() function — where XYZ is the name of the Permission Area (adding a Permission Area also adds the corresponding function) — to determine if the specified user has access to that Permission area. With this technique, users do not see navigation to areas that they cannot access; if users try to access those areas (for example, if they bookmarked a page in a restricted area and then logged out), they get an error message.
In some situations, I need to go a step further. For example, when I use the AccountSettings Web Screen, I do not want to just make sure that the user is an AccountAdministrator, I need to make sure that the user is the AccountAdministrator for the account in question; after all, it would be trivial for an attacker to make an account and access the AccountSettings Web Screen, but pass to it a different AccountId to edit. One option is to have the AccountSettings page use the AccountId of the currently logged in user, but I wanted slightly more complex logic. I chose to use an If Widget in my Preparation that checks to see if the AccountAdministrator identifier in the ACCOUNT entity is the same as the current user's ID, or if the user has the GlobalAdministrator Permission; if neither of these conditions are met, I throw an Exception that I created for this purpose.After poking around in the documentation, I learned that the best way to handle all of these miscellaneous Exceptions was to go to my Screen Flow and add ErrorHandler Widgets (Figure B) to pick up the Security Exceptions (which are generated when a user tries to access a Web Screen for which they do not have authorization) and the InsufficientPrivileges Exceptions that Rat Catcher produces. From here, I created a Web Screen that tells the user that they lack permissions and had the InsufficientPrivileges Exception Handler link to this screen. For the moment, the Security Exception Handler also points to this Web Screen, but I am working on a Login Required page that will redirect the user to their original destination after a successful login. I also took the opportunity to create a generic "an error has occurred" page that automatically submits a ticket to me on the user's behalf. This only took a few moments because I already had the functionality on my Help screen — all I needed to do was refactor it into a separate Action. Figure B
The portion of the Rat Catcher Screen Flow that handles the errors.
Now that Rat Catcher has valuable information such as passwords and credit card details flowing around, it is very important to get some encryption occurring "on the wire" to protect the users' data while in transit. I have the Account Purchase screen made, but it is not hooked into a credit card provider yet because this encryption is not in place. A prerequisite to get a credit card gateway account is PCI compliance, which requires SSL security, so it has become really critical that I get the SSL encryption hooked up.
On the Agile Platform end of things, this is a snap. All you need to do is select a Web Screen that will transmit sensitive data, select the HTTP Security drop box, and choose SSL. If you have a need to verify the identity of your visitors, you can choose SSL With Client Certificates. On the server side of things, you need an SSL certificate from a trusted source to be installed on the Web server and have the server use that certificate for SSL sessions. If the application is strictly for internal use, your IT department can use their in-house Certificate Authority to generate the certificate for free. For me, I had a bit trickier route to take, due to having the server behind a reverse proxy.
First, I had to get my certificate. I chose Go Daddy due to its ridiculously low prices, great customer service, and past experiences. I promise that the company's racy advertisements did not influence my decision (I became a Go Daddy customer in the middle of a 15 year period of not watching TV)! I wanted my front-end server to be able to dish out other sites via SSL as well, so I chose Go Daddy's Standard UCC certificate, which allows additional domain names in the certificate. Next, I installed the certificate, added SSL to my sites' bindings, and chose the certificate.
Then I discovered that my URL rewriting setup wouldn't work because, when the Agile Platform Server would write out the HTTPS version of URLs to secure them, it used the hostname that it was called by, not the original one. So in my case, even though the user had pulled up the site by the external hostname, the output was using the internal hostname for the SSL links and form submissions. This was a problem. I deleted the general Reverse Proxy rule that I made, and I used the Reverse Proxy template in the URL Rewrite configuration of IIS Manager to use the standard reverse proxy. Now I had an entirely new set of problems — none of my proxying was working. It turns out that I needed to authorize the use of the HTTP_ACCEPT_ENCODING server variable and force it to always be empty; otherwise, the rule to rewrite the URLs that the backend server was generating would fail. This fixed the system for the unencrypted pages and made the links to the encrypted pages be correct.
My attempts to pull up the encrypted pages were still failing with a bizarre error code of 502.3 ("bad gateway"). To make a long story short, I needed to go into the backend server, bring up the virtual directory for the eSpace, and tell it to "ignore" client certificates instead of accepting them. Yes, this breaks the possibility of using the HTTP With Client Certificates option on a page; the alternative was a long, painful process to install a client certificate on the behalf of the local machine account on the front-end server. Looking at the instructions made me queasy, and since I never plan to require client certificates, I chose to take the easier route for the time being.Figure C
Even though the current page uses HTTP, the Create An Account link points to an HTTPS URL because that page requires SSL.
Enterprise ManagerEnterprise Manager is a component that allows your applications to share the user databases, be centrally managed and administrated, and more. It comes with a lot of built-in functionality around the authentication and authorization process; it is also home to some of the other components in the Agile Platform that I like, such as Embedded Change Technology (ECT). Figure D
The Enterprise Manager's user management functionalityIf you were smart enough to start your project with the Style Guide (Figure E), you are already integrated into Enterprise Manager. I wasn't that smart. Being the headstrong guy that I am, I dove headfirst into this project and took the bull by the horns. In this case, it meant that by the time I even learned about Enterprise Manager, I had a lot functionality built and dependent on my login system. I tried to merge Rat Catcher with the Enterprise Manager Template eSpace, but it came out pretty badly. I took a step back, and I asked myself if Enterprise Manager was worth it. If I started a project from scratch again, the answer would be yes, but given that Rat Catcher does not need to integrate into other applications (it is the front-end UI and billing system for a WCF Web Service), I felt that I had already duplicated much of the functionality that I would have wanted from Enterprise Manager. Faced with the decision to keep on trucking with what I had or to merge the two systems, I chose to keep on going my way. But from here on out, I've learned my lesson: all new projects start with the Style Guide and use Enterprise Manager. Figure E
When you start an application using the Style Guide, it already includes screens for working with users, such as this login screen.
J.JaDisclosure of Justin's industry affiliations: Justin James has a contract with Spiceworks to write product buying guides; he has a contract with OpenAmplify, which is owned by Hapax, to write a series of blogs, tutorials, and articles; and he has a contract with OutSystems to write articles, sample code, etc.
Justin James is the Lead Architect for Conigent.