Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add role based output (think membership sections, RBAC) #5139

Open
bep opened this issue Aug 30, 2018 · 27 comments
Open

Add role based output (think membership sections, RBAC) #5139

bep opened this issue Aug 30, 2018 · 27 comments

Comments

@bep
Copy link
Member

bep commented Aug 30, 2018

Note that this proposal isn't a "ready to implement" one. These are some initial thoughts. And these thoughts skip the parts that live outside of Hugo (authentication etc.).

The current options for this with Hugo include

The downsides of the above are

  • It can be very complex and potentially very expensive
  • It only allows you to allow or restrict access to a set of resources, i.e. URLs. E.g.: "You have to have the role MEMBER to view pages below /member", "You have to be ADMIN to view pages below /admin" etc.

If you want to, say, show a different home page for members, you will have to go the cumbersome route of somehow building your pages with secured server-side services. Which partially removes the static part and you end up with some ... jam.

This proposal is simple:

  • Add Role as a new concept in Hugo
  • There should be a Role hierarchy
  • It should be possible to override templates for a given role, e.g. home.member.html.
  • It should be possible to do role membership checking in the template: {{ if .Site.HasRole "member" }}{{ end }}
  • A way to restrict output for roles.

The above is simplified. There are some questions on how to do this effectively with many roles to avoid duplication etc., but in it's simplest form with 2 roles you may end up with something like this:

site
└── public
    ├── member
    │   └── index.html
    └── user
        └── index.html

The above will solve both authorization (you need to be a member to have access to resource x) and presentation (if not a member, show the registration link).

The above would still need something outside of Hugo to do the switching (possible solutions include Cloudflare Workers or Cloudfront Lambda@Edge). But the above is a much simpler model to reason about than what we have today. You could possibly set up a Amazon S3 bucket per role?


Some additional notes:

Adding roles as a front matter attribute to restrict access would work and would be the to go for many situations, but it would not be very convenient for "big" sites, and it would also not cover static assets.

So I suggest that we also add some "access patterns", e.g.,´/members/**` etc.

@bep bep added the Proposal label Aug 30, 2018
@regisphilibert
Copy link
Member

A way to restrict output for roles.

Yes also have to consider pages which bear no "user" version and which are solely available to members (certain roles).

This could be as simple as adding a role Front Matter for single page config or a more global section configuration in the like of URL rewriting:

# content/welcome-member.md
---
title: Welcome Member!
role: 
   - member
# config.yaml
roles:
  videos:
    - member
    - premium

@talves
Copy link

talves commented Aug 30, 2018

At first glance, Couldn't the structure be handled already with roles being setup as a category or tag, then having the template watch for that category and use the partial based on that role? The path for the role would just be published to the membership service as needed.

@regisphilibert
Copy link
Member

@talves I'm sure it may already be possible using unrelated features and hacks, but what Bep is proposing here is a "supported" feature along with documentation and a defacto "best practice".

I can sense a lot of people will be glad to let the markup (Hugo) handle this "role" conditional and publish dir structure rather than relying solely on another part of the JAMstack.

@talves
Copy link

talves commented Aug 30, 2018

@regisphilibert Yes, I get it.

@bep
Copy link
Member Author

bep commented Aug 30, 2018

What I have in mind isn't currently possible with Hugo. You may achieve something similar (I think Custom Output Formats may come closest), but even then it would be a band-aid solution you're not ... trusting. For membership type of information, some information leak between sections may not be too bad, but in many situations when you want to introduce role-based access, you want a well-tested and ... secure solution.

@Brixy
Copy link

Brixy commented Aug 30, 2018

👍

Very interesting and promosing, and another reason for many to move away from WordPress and Co.

you want a well-tested and ... secure solution

Are you thinking of JavaScript or anything third party dynamicish?

@bep
Copy link
Member Author

bep commented Aug 30, 2018

Are you thinking of JavaScript or anything third party dynamicish?

My sentence above was targetted at a "Custom Outputform home made" variant of the above. But also, doing this with a fine-grained JavaScript/JWT rendering logic is 1) Hard to implement but also 2) Hard to verify/test. With my proposal you still need authentication -- but the authorization part is on a very coarse-grained level. Apache/htaccess etc. would get you very far.

And having these "role file sets" on disk before you publish means that you can do "static security audits", so to speak.

On some level, it sounds too simple and a little bit stupid to actually work. But the simplicity has many nice features that I like more and more when I think about it.

@danfascia
Copy link

This has legs, big time...

Imagine the combo of something like Auth0 + Hugo tokenised content delivery could be rather sweet. There's obviously a lot to think through here but I'm liking the way your (great) mind is whirring on this one.

@bwklein
Copy link
Contributor

bwklein commented Aug 31, 2018

Here is something using Auth0 from @biilmann https://github.com/biilmann/auth0-example

@stale
Copy link

stale bot commented Dec 30, 2018

This issue has been automatically marked as stale because it has not had recent activity. The resources of the Hugo team are limited, and so we are asking for your help.
If this is a bug and you can still reproduce this error on the master branch, please reply with all of the information you have about it in order to keep the issue open.
If this is a feature request, and you feel that it is still relevant and valuable, please tell us why.
This issue will automatically be closed in the near future if no further activity occurs. Thank you for all your contributions.

@stale stale bot added the Stale label Dec 30, 2018
@regisphilibert
Copy link
Member

Hope this is kept.

@stale stale bot removed the Stale label Dec 31, 2018
@bep bep added the Keep label Dec 31, 2018
@bep
Copy link
Member Author

bep commented Dec 31, 2018

Yes, this is still a really good idea.

@RickCogley
Copy link
Contributor

Yes! An easy way to achieve this would be so sweet and welcome.

@bep
Copy link
Member Author

bep commented Jan 11, 2019

Yes! An easy way to achieve this would be so sweet and welcome.

I agree more and more.

I saw a blog post from Netlify about "how to use GatsbyJS for real application" (not sure what the title was exactly), which in some steps showed how you could add authentication to a site and then use the credentials in Netlify functions.

I think that was the gist of it, but my main thought after reading that was:

  1. Authentication isn't the hard part in this. The are plenty of simple to use authentication providers, you can delegate to Google/Facebook etc. if you want.
  2. If you move the authorization part to Netlify functions you 1) Have moved away from many of the benefits of static 2) Have moved pretty close to the WP land you tried to move away from 3) Get a potentially very expensive site to run at scale (see https://www.netlify.com/pricing/)

So, while my suggested approach to this may look almost too naïve to work, but the mental model is extremely simple, and the added cost is almost entirely build time (I think Hugo could pull that off if we are smart). A "membership site" with lambda functions or similar isn't realistic for anyone with a limited budget, and even then it sounds suboptimal.

@RickCogley
Copy link
Contributor

RickCogley commented Jan 11, 2019

I'm not yet using Netlify, but it appears that for the free level, there are add-ons if you need more than the 1000 identity users included for free. It would be 99 USD / mo for 1001 to 5000 users. Otoh, Auth0 free level allows 7000 users for free. They both have different constraints. Anyway, the assumption with those is, at some point, it's going to cost. I'd rather find a forever-free or nearly-free way to do it, for a small site.

It would also be nifty if, your "s3deploy" could be made to deploy as needed, i.e. to whatever buckets map to whatever role, etc.

See also: https://github.com/jsonmaur/lambit (yet another node app but...maybe good for reference assuming lambda@edge)

@bep
Copy link
Member Author

bep commented Jan 11, 2019

It would also be nifty if, your "s3deploy" could be made to deploy as needed, i.e. to whatever buckets map to whatever role, etc.

There are some fairly concrete plans (if you watch the PR list) on getting a tool with a similar feature set of s3deploy (but more generic, i.e. not only Amazon) integrated into Hugo. That means that we can easily get a more integrated solution so you can simply do hugo --deploy or hugo deploy.

I have not done price calculations of the above, but In my head, the expensive and complex part in the "existing model" would be the dynamic fetching/authorization of content:

Add packs of 500k requests and 500 runtime hours $19/each

You would some way to map users to role(s), which is not described above. The natural thing would be to use "some authentication provider" that supports this, but for simple setups maybe Hugo could maintain a simple mapping ("bjorn.erik.pedersen@gmail.com => admin") and provision some "lambda storage" ... Me thinking out loud.

@RickCogley
Copy link
Contributor

Interesting! If it could be made to work to keep that mapping in a json data file, then managing it could be done anywhere you like, and, you could likely simply script a curl to GET the latest json via db X's API before you build, and write it to, say, /data/rolemap.json (or somewhere resource-y), then have Hugo use that.

[
  {
    "bep@gmail.com": "admin",
    "randoman@gmail.com": "user",
    "baddie@hak0rz.com": "none",
    ...
  }
]

@bep
Copy link
Member Author

bep commented Jan 11, 2019

Interesting! I

Yes, but not very dynamic. But it obviously could work for some use cases.

But note that Hugo can not use that "role map" directly. We are a static site generator, so any role validation must happen in the "lambda router" (or something).

@lionzan
Copy link

lionzan commented May 5, 2020

Hey guys, did we do anything on this? I'm desperately looking for a solution. I love Hugo and I don't want to switch to say Gatsby, which actually does it see https://www.gatsbyjs.org/tutorial/authentication-tutorial/

@bep
Copy link
Member Author

bep commented May 6, 2020

@lionzan no, but note that this issue is mostly about authorization; Gatsby does not provide a (static) way of doing that?

@lionzan
Copy link

lionzan commented May 6, 2020

@bep thanks. I'd be totally new to Gatsby if I ever started to explore it, that's what I was pointed to as an example to manage RBAC on a site built with one of the stacks integrated with Amplify.
So I think I will stick to Hugo on Amplify, yet looking for a workaround or hack to make work RBAC on specific sections or pages of the static site.

I was thinking I could use insertHTML getting the content to be inserted from specific folders under /static/ depending on the role of the user, and limiting access to the folders to specific roles.

This would of course require me to create some additional files and folders under /static/ in some other way not directly controlled by Hugo.

Does it make any sense? Or am I missing some crucial aspect?

@bep
Copy link
Member Author

bep commented May 6, 2020

Does it make any sense? Or am I missing some crucial aspect?

I cannot answer that without writing a novel. I read your link again, and it mostly talks about authentication -- which is the simple (relatively) part of all of this. The small part about presenting "user based" content is very much dynamic -- and not remotely similar to the static role based setup outlined above.

@lionzan
Copy link

lionzan commented May 6, 2020

I think that the authentication part is relatively straightforward with AWS cognito which integrates perfectly with Amplify.
I agree that serving the right content is much more complex and beyond the scope of Hugo, which by its nature does not "serve" anything after building. The build is in fact static!
I guess that the point is about how dynamic is the dynamic content. For me it is about serving N different versions of the same page depending on the user being associated with one of N roles.
Do you think that the static setup I outlined could work in such simple scenario?

@bep
Copy link
Member Author

bep commented May 6, 2020

I agree that serving the right content is much more complex and beyond the scope of Hugo, which by its nature does not "serve" anything after building.

If you read my first post in this thread, you will see that I don't agree about this. I think this will be a big win for typical membership portals (small newspapers etc.).

@lionzan
Copy link

lionzan commented May 6, 2020

Yes I read it, when I say beyond the scope of Hugo I mean it "right now" and I agree with you that it should be a capability of Hugo.

My problem is that I want to do it now, not wait till your proposal is implemented and integrated in the system.

As it stands, Hugo has no dynamic capabilities whatsoever as far as I understand, but I might be wrong. (actually from what I read in your profile, there's no way you can be wrong when talking about Hugo! Thanks for developing it!)

@mireille-raad
Copy link

I am not sure where we are on this, but eleventy seems to have an interesting approach.

https://github.com/11ty/demo-eleventy-serverless-oauth
https://www.zachleat.com/web/eleventy-login/

@bep
Copy link
Member Author

bep commented Nov 9, 2022

@mireille-raad I don't see any mentioning of roles in the above links?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants