How do I password protect an entire page?

Happy New Year.

How do I protect an entire page?

I have built my admin panel where I can make changes to user accounts etc and would like to protect this page from everyone other than the admin.

Can someone tell me how?

Mike

The way I did that for myself is simply put on page load if user is not my email then go to home page. You can also hide the groups on the page unless it’s your email as the user as well.

Well, that’s how I did one for myself. There might be a better way to do it out there.

In your APP Database create a User email address field and admin (yes/no) field. Then you register to your application with your email address and login to your application.

Workflow: {When page is loaded} and {Current User admin value = "no"} redirect the user to {PAGE}.

The above solution would make it easier to manage regular users with yes/no for admin value.

5 Likes

With the addition of the workflow ‘on page load’ checking if the current user custom field admin = yes it also might be good practice to have a master group with all the elements inside. Then on the master group you can uncheck ‘this element is visible on page load’ and setup a condition.
This condition would be similar to the workflow page load, where when the custom user field admin = yes, then show the master group (this element is visble = checked).

This will be an extra layer of security incase a network error causes the workflow not to trigger or any initial page load lag.

4 Likes

@luke2’s suggestion is essential. For any “controlled access” type of page – whether that’s an administrator-only page or a page where a registered user makes/changes data, etc. – you usually want to keep all page elements hidden by default and only show them via the page workflow(s) that check that the current user is authorized to view them.

Additionally:

There are many different ways of handling administrator access and a couple have been mentioned above, including:

  • If you’re the only admin, check for Current User’s email is your email address. (This is what I’d call an implied administrator approach.) Similarly, you could check if Current User’s unique ID is your unique ID. [EDIT: These can work for page access, but will not work in data privacy rules as user @luke2 discovers in a later reply.]

  • Have a boolean (yes/no) field on the User object called “Administrator” and set it to yes for Users who should be allowed admin access. (This is an explicit administrator approach. But I’m not sure this is particularly secure, actually, and it would be very easy to accidentally give admin access to some random user.)

Another way to do this – which I personally use and like a lot – is to create what you might call an Administrator “role”. Even if your app doesn’t have a concept of roles, you can do this very easily.

What I do is this (and it’s super secure):

What we do with this object is check if the results of Do a Search for... Administrators contains Current User. If this is true, the User is an Administrator. Read on…

The only way to become an Administrator is for a User (YOU, in this case) to make an Administrator object. Of course this is only possible if you make a workflow in your app that allows this. So, on an admin type page in your app (Dev Mode), drop a button. Give it a very simple workflow:

When Button is clicked... Create a new thing... what thing? Administrator.

Preview your page (and log yourself in of course if you are not logged in) and click the button. Congratulations, you just made yourself an Administrator (in your Dev database)!

You could confirm this by examining your Data > App Data tab like so:

In a moment, we are going to delete that all-powerful button. But first: If you’ve already pushed your app to Live, you have another copy of the database there and your User object may or may not have the same unique ID in both databases. So you either need to (1) push Dev to Live and click that button in your Live mode app. Or alternatively (if your User object is the same in both databases) you can (2) later copy just the Administrator data type from Dev to Live using the database copy features (use caution of course that you only push that new data type – don’t accidentally overwrite your whole database, duh).

If you’ve never pushed your dev app to live, you don’t need to do anything else as, presumably, someday you will push your dev app to live and also copy the database at that point. (And so your User object and this Administrator object will exist in both dev and live versions of your app and be properly associated with each other.)

Now:

Immediately go back to Edit mode and DELETE THAT BUTTON! Now, there is only 1 Administrator in your database (YOU) and there is no way for anyone else to become an Administrator. In fact, even you can’t accidentally create another administrator.

If you had to click the button in Live mode, don’t forget to immediately push dev to live so that the button is also gone from the Live version of your app.

Now we can make this object even more secure. Go to Data > Privacy tab and select the Administrator type. Set up a privacy rule like this (when This Administrator’s Creator is Current User, enable all access. For everyone else, there are NO permissions at all):

This gives the Administrator objects a unique property: They can only be seen by the User that created them. In fact, even another Administrator cannot see who the other Administrators are. The only way to see any info about Administrators is in the back end of your app, in the Data tab. Groovy, right?

Now here’s how we use it. Your page load workflow that checks for Administrator access is like this:

This Search will only return a value when (1) the Current User is logged in and (2) when the Current User is the creator of an Administrator object. It returns nothing - zero, nada, zilch - for other users and will always evaluate to false. And this condition cannot be hacked or manipulated in the browser. It is ultra secure.

But if the Current User is logged in and is an Administrator, this condition evaluates to true (and also reveals no info about any Administrator but themselves).

So in the example above, we show “Group B” which is just a group (set to NOT visible on page load) that contains everything of interest on this administrative page.

For the fail case (User is NOT an Administrator), we just do something like this (redirect somewhere):

Now, as mentioned previously, there’s no way in your app or in your database to successfully create another Administrator except by executing a Create New Thing… Administrator action as the User you want to give admin rights to.

So, if you need to add other administrators, you recreate the magic button and click it on that User’s behalf (using “run as…” User). The high-security way to do this is just create a page that is only viewable by that User (for example “When Current User’s unique ID is [paste that user’s unique id]” show the page, otherwise redirect), put the button there, run the app as that user, click the button, then go delete the page (and if you’re doing this for Live app, don’t forget to push an update so that the page is gone from Live as well).

This sounds like a bunch of rigamarole, but you’re probably only ever going to do this once (for yourself) or maybe a couple of times (for close collaborators).

And then you just don’t ever need to worry about it again and it’s super easy to secure any admin pages, work-in-progress pages, etc. You just drop either or both of the “Do a Search for Administrators’s Creator contains/doesn’t contain Current User” conditions on any page you want to protect, as appropriate.

[EDIT: For more about how you can use this Administrator role as part of other privacy rules – and whether or not this is a good idea – see my further reply down the thread: How do I password protect an entire page? - #7 by keith]

15 Likes

Thanks for this post Keith about using the ‘Administrator’ data type, this is most useful.

With regards to the 2 suggestions you made as a simplified version and for my case, where I just have one user who needs direct access to a data type e.g. invoices, I can’t seem to get either to work on the Privacy Roles.

So for the current user’s email is, I can’t actually find the field in the search, just ones I’ve built in:
image

And for the current user’s ID is, this I can access, but only enter numbers, even pasting in, it will strip down the characters and remove the numbers, which I find quite odd.

image

Is the functionality missing from the Privacy Roles? I am a bit sleep deprived so apologies if I’m missing something :wink:

EDIT: Sorry :man_facepalming:, realised the field I mentioned above, ‘ID’ with the data type ‘User’ is something I’ve created and not the actual ‘Unique ID’. So it seems both ‘Email’ and ‘Unique ID’, the built in fields are inaccessible on the When search right?

Cheers

Hey @luke2, you’re not missing anything. Privacy roles have their own rules in terms of what expression you can build for them. (I’m not sure if those rules are entirely documented, but they may be.)

In another thread I had mused about Privacy Roles as a way of potentially limiting which objects might be downloaded to the page, based on more temporary comparisons. And I noted that there are expressions that cannot be built directly there, but that one can build elsewhere and paste in.

It was explained by the Bubble team that this is a bad idea and prone to breakage. (So, “don’t do that.”)

It’s true that it seems you cannot do comparisons here to the unique identification fields of the User type (User’s email, User’s unique ID). I suspect this is because those may be subject to change and hard coding them is a bad idea.

There is another reason that this is a bad idea: It holds the potential to be VERY confusing to you as you build your app further. Having a superuser role like this will break all sorts of business logic when you are using the app as that user.

You can achieve what you want though if you create that Administrator type exactly as I describe above (do it first) and take one extra step: put the Administrator object on the User. (User must have a field “Administrator” of type Administrator. Set that field to be the Administrator created by clicking the magic button. That is, create the new Administrator and immediately attach that administrator to User.)

Set up the privacy rule for Administrator as I describe.

Now on some other data type, you can evaluate:

This may seem like a strange and insecure thing (but I’ll explain why this works below). Now YOU can view any Task, even if you would normally have no right that Task. (Imagine that Tasks have a privacy rule such that they are only visible to the Task’s Creator. You will not pass that condition, but you will pass this one.)

To understand why this works (and why this might be a bad idea), imagine what happens when you are logged into your app as this superuser/Administrator:

Let’s say you now go and visit some page that shows us the detail of some Task. Let’s say it is Task 123x456 (I’m abbreviating the unique ID here obviously for sake of argument). This task was created by User “jenny” and should only be visible to her as Tasks are only visible to their Creator (and the superuser):

https://myappexample.com/view_task/123x456

You will not pass the test Current User is this Task’s Creator. But what happens when the Administrator test is applied:

Bubble goes to evaluate Current User’s Administrator. You do have a value there. It is that Administrator object created by the magic button. Can you retrieve that value? Yes you can as you are that Administrator’s Creator (remember that that’s the only rule on the Administrator object). So, Current User’s Administrator is not empty and you pass the test.

The page renders for you with all of the detail of the Task.

Let us now imagine that User “bob” comes along and tries to view this page. Bob has never been given the ability to create an Administrator and so the Administrator field on his User object will be empty. He will not pass the Administrator test. He will also not pass the Task Creator test.

The detail of this page’s task will not render for Bob.

Let us now imagine that you made a screw up somewhere. That you accidentally in some workflow DID populate Bob’s Administrator field. You accidentally attached YOUR Administrator object to Bob’s Administrator field. Can Bob now see this task?

Here’s what will happen:

Bob will not pass the test Current User is this Task’s Creator (only Jenny can pass that test). Will he pass the Administrator test?

Bubble goes to evaluate Current User’s Administrator. Bob does have a value there. It is that Administrator object created by you. Can Bob retrieve that value? NO, he cannot, as he is NOT that Administrator’s Creator. So, Current User’s Administrator WILL COME UP EMPTY and Bob does not pass the test.

So again we see how the Administrator approach I describe previously is somewhat doofus-proof.

However, can you see why this might be a bad idea now? Let’s say you are visiting this page because Jenny has reported a problem. It just doesn’t work right for her and it’s not due to the Task Creator test, but something else down the line that does relate to some privacy rule being configured incorrectly. If YOUR User object has this magic Administrator property and all of your data objects allow you this magic permission, you’ll have a devil of a time debugging Jenny’s problem – when logged in as YOU. Things behave very differently for you.

So you really, really, really have to be cognizant of that.

In the scenario that a user reports something is not working for them, you really have to test your app logged in as them (use “run as…” from the data tab). Which is what you should do anyway, regardless of whether you have a superuser mode or not.

So, I’d advise being judicious with this sort of privacy rule. It’s very useful of course for the User data type.

(You want an admin page where you can view all users of course and you might also want to view everything about them without having to visit the Data tab. But you still have to be mindful of how this might affect core business logic as you are testing your app in your own user context. It’s easy to build stuff that works just fine for you and forget to test it as a not-logged-in user and also for a non-superuser and just deploy away and “whoops, none of that shit works right for anybody else.”)

2 Likes

Hey @keith

Cheers for the detailed reply.

Ah yes sounds like a bad idea pasting in an expression generated outside of the Privacy section, as easy as it might be :wink:

Yeah now you say it, it makes sense why perhaps hard coding the email address or unique is off limits, could be as well be related to speed at which these privacy rules need to be constructed at along with keeping the privacy roles clean and simplified.

Yes thanks for clarifying, the Administrator data type method you’ve covered, makes sense and seems like the most secure way to handle an admin type role. Could be quite easily scaled if more people come onboard internally e.g. some sort of permission levels

So a final expression would be - just double checking this is the right approach?


(I added the ‘Administrator’ data type, into the User and then added the corresponding ‘Administrator’ entry)

Noted there, I’ll air on the side of caution about which user account to use when debugging a customers account for any potential issues. Like you said, the ‘Run as’ the user in question is the go to option.

Cheers

The rule can be as simple as I have it above:

Current user’s Administrator is not empty

wow, wow, wow

@keith - Thank you so much for your effort on this. It is greatly appreciated.

So far so good until I get to the workflow.

When I click a do a search for administrators - creator does not show up as an item, only as an “each creator”. Even though, the administrator object has my user email as the creator.

Am I missing something?

Thank you so much!!!

[EDIT] The reason why this is an issue is because I will have two administrator’s not just me.