MarkLogic, Angular, and node: AuthenticationAuthor: Dave Cassel | Category: Software Development
My first effort with the DemoCat application had a pretty simple architecture: node hosted static resources and passed REST requests straight through to MarkLogic. This approach is more in keeping with the three-tier architecture that MarkLogic’s customers typically use, but my middle tier was doing almost nothing. I’ve made some improvements, especially related to user management. I should point out that I’m new to node, so if this looks off-base, let me know, but this looks like moving the right direction.
What’s Wrong with Pass-through
The problem with this approach is that it effectively exposes the MarkLogic REST API to the end-user, which is a security vulnerability. (Application Builder directly exposes the REST API, too, with no middle tier.) MarkLogic does have a robust security model; it’s possible to lock down an application using that model, but there are a few reasons why I like doing so in the middle tier.
Role versus User Permissions
Consider the documents in which we store user profiles. In Demo Cat, I’m storing the user’s full name and a list of email addresses that the user has access to. In MarkLogic, we set document permissions with roles. Users who belong to a role that “update” permission may update a document. That means that to let my user (dcassel) update my profile, but not update another profile document, I would set up a “dcassel” role with update permission on the profile document. Only the dcassel user would get the dcassel role. This would work, but it isn’t in the spirit of how roles are intended to be used; rather, the intended use is that a role represents a group of users.
Note: there are certainly applications out there where roles were created for individual users, and that was done for legitimate good reasons. It’s not that you can’t or shouldn’t, but if you find yourself creating roles for individual users, it’s worth taking a look to see whether another way makes sense.
There are other ways to limit who can update a profile document. For instance, I could create an extension specifically for doing so and have that extension check whether the current user matches the profile’s owner. However, doing that would still leave a backdoor open, in the sense that /v1/documents would need to be locked down.
A big difference between limiting access to the MarkLogic REST API versus using internal security to limit what can be done with it is the mindset we use to approach it. If we directly expose the MarkLogic REST API to the end user, we’re in a black-list mentality — we need to figure out what to shut down. But if deny direct access to the REST API, we can approach it as a white list, deciding which features to expose, how, and to whom. To make an application secure, that makes more sense to me.
Mindset Part 2
Another note on mindset — MarkLogic is capable of hosting applications completely, without using a middle tier at all, but some customers don’t want that. They prefer to have a business logic tier with a hard line on a diagram connecting it to the database, rather than the database actually hosting the middle tier. (For anybody who read that and thought, “who would do that?”, when you have such a capable language running at the database level, you really can express all your business logic there. But I get it, you want more distance between the layers.) When you design a system with distinct business and database tiers, you’re not going to turn around and expose the database tier directly to the end user.
MarkLogic Security Still Useful
You should definitely not think, by reading the above, that MarkLogic security stops being useful when you introduce a middle tier. Far from it! With MarkLogic security, you can decide which groups of users (roles) can see which documents, then these restrictions are accounted for when performing searches. You can also easily prevent unregistered users from creating or deleting content, even in cases where your middle tier doesn’t know enough to decide what’s allowed. You have multiple levels at which to apply security, allowing you to focus on content (documents, directories, etc) or access methods (API).
Middle Tier Access Control
Okay, locking down the REST API makes sense, but does that mean we need to write custom endpoints for every touch on the database? For some systems, the answer is probably yes. Given that my goal is rapid application development, but trying to be on a path toward production applications, I’ll take the middle road. I want the client code (written in AngularJS) to be as portable as possible from one application to another.
Node.js hosts my static resources and exposes endpoints for functionality. For the sake of portability, my AngularJS code will make requests to REST API URLs. However, those requests go to node, where I can decide what to do with them. Consider this function:
Session management is done at the node layer, so if the user hasn’t logged in, no PUT requests are allowed. If the user has logged in and is PUTing a user profile, the profile has to be the one that belongs to the user. (I haven’t yet written code that will update the in-memory user profile based on the updates that have been sent.) The proxy() function simply relays the request on to MarkLogic, complete with credentials identifying the user.
You can see the rest of the middle tier at Demo Cat’s GitHub. You’ll see that GET requests are proxied right through (at the moment, I’m intending to change that); PUT and POST require the user to have logged in; DELETE requests are not supported at all; and a few /user/ endpoints govern logging in and out.
Locking Down MarkLogic’s HTTP App Server
The last step in controlling access to the REST API is to prevent people from bypassing your application and going straight to the REST API’s app server. This can be accomplished simply by having a firewall not allow external access, or you can set the MarkLogic HTTP app server’s address field to “localhost”, thus only allowing requests from the same server that MarkLogic is running on.