gohugoio / hugo

The world’s fastest framework for building websites.
https://gohugo.io
Apache License 2.0
74.73k stars 7.45k forks source link

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

Open bep opened 6 years ago

bep commented 6 years ago

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

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:

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.

regisphilibert commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

@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 commented 6 years ago

@regisphilibert Yes, I get it.

bep commented 6 years ago

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 commented 6 years ago

👍

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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

stale[bot] commented 5 years ago

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.

regisphilibert commented 5 years ago

Hope this is kept.

bep commented 5 years ago

Yes, this is still a really good idea.

RickCogley commented 5 years ago

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

bep commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 4 years ago

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 commented 4 years ago

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

lionzan commented 4 years ago

@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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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 commented 1 year ago

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 commented 1 year ago

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