User-proofing critical data (while maintaining privacy and performance)
Published on Oct 14, 2016
Sometimes you want to let a user access certain information, and influence that information in certain ways, without giving them free reign to do whatever they want. Ember Data isn't built for that use case, but there are ways to work around it.
Today's example is the user's score on EmberScreencasts.com. We talk about 2 rejected solutions, then walk though the code for the solution that we ended up with.
- EmberSchool.com (Log in to the free demo and check some completion boxes to see the score feature)
This video is mostly conceptual, and your code will vary wildly depending on the data and server you're working with.
This last weekend, I added a new feature to EmberSchool. It’s this score right here. So what it is it’s the total of the score of all of the lessons that you’ve completed. So see the score changing when I check and uncheck this box. Then you go on, you complete more, and the score increases. This may look simple but there was a surprising amount of thinking behind it. There were three different implementation options that I considered. And in this video, I’ll go over the first two and why they weren’t sufficient, and then we’ll look at the code for the third, including some of the server side code.
So the first option is to calculate this on the client side every time. So your page loads and then your user loads, all your lesson completions load, and then you sum them all up. That has the benefit of simplicity. It’s very easy to think of that solution. But the downside is that it requires you to load every single item that goes into this. And right now that’s just my personal lesson completions and it’s not that big of a deal. But we could be adding other things to the score later on, things that take even longer to load, and this becomes a bigger deal as well when there are more courses. And then let’s say you want to have some sort of leaderboard. Now suddenly you’re downloading all the lesson completions of every single user in order to calculate it and then rank them. That aside from being a performance nightmare could violate some implicit privacy agreements. So people probably aren’t going to get too worked up about their score being displayed, but if someone else can see exactly which videos they’ve done or later on exactly which actions they’ve taken, I could see people getting pretty offended over that.
So what about the second option I talked about? And that is simply to have it as a field and just set it. Every time you want to change it, you just add to it. And that will work if there’s no reason people would ever want to change that, if there’s no advantage to improving that number. But let’s say you want to go and... we’ll go to the user, and here we go, we found our user... We don’t want 370 points. We want 370,000 points. Boom. Check it out. And if we didn’t have any safeguards on the server, then people could just have whatever number of points they want. So we don’t want that. What we’re going to end up doing is taking a hybrid approach that offloads some of this to the server. Let me show you.
So the first thing you’ll notice is that we do have a
points attribute on the user that we get from the server. And then whenever we mark a lesson complete, or mark it incomplete, then after saving this lesson completion then we get the
currentUser and we call
changePoints on it and feed in the
completion. And then we increment and decrement the property based on the number of points that are in that lesson. And you may be thinking, isn’t this exactly what we just said we shouldn’t do? Well here is the part that makes it better. So here when we’re seeing how to update the user on the server, you’ll notice if you try to update the number of points, if you save the user with those points change, then it won’t take. The number of points will not actually change. What we’re doing here with
changePoints is basically a convenience for the user so that the change in the points shows up immediately, and so that we don’t have to reload the user to get the new number of points, because nothing that happens here will affect the server at all. What happens in the server is whenever you create or update a lesson completion, and that’s what happens when you click that little checkbox, then it’ll call
calculate_points on the current user. And what that does is it takes all of your lesson completions that have been marked true and it sums them up.
So this is what we have been talking about doing on the client in the first solution, but now we’re doing it on the server, and instead of doing it on read whenever we’re trying to read the number of points, we’re doing it on write, all the times when we’re trying to change the number of points. So we calculate it and then we save. And what that means is that whenever we are sending out a user, it’ll come with the number of points that are there and we don’t need to send out any of the lesson completion data, or in the future possibly more sensitive data. And that is how you can let users access as well as indirectly change sensitive data without giving them free reign.