Combination of roles in Episerver - roles

I have a site where our customers log in to see their own data. Each customer must only see their own data (of course), and different users will have access to different pages within one customer. In addition - the editors must see all data.
I want to set up the access rights based on roles to determine which customer that the user is member of, and what pages the user can access.
Groups:
Customer1Role
Customer2Role
TicketViewerRole
ChangeRequestRole
Users:
Cust1_LowLevelUser. Roles: Customer1Role, TicketViewerRole
Cust1_HighLevelUser Roles: Customer1Role, TicketViewRole, ChangeRequestRole
Cust2_LowLevelUser. Roles: Customer2Role, TicketViewerRole
Cust2_HighLevelUser Roles: Customer2Role, TicketViewRole, ChangeRequestRole
Page structure
We have created a page tree where each customer has its own "root page" with access only to their respective role. Below that node we create instances of the data specific pages, which have their access rights based on user roles as well as the customer role.
Customer1 (Customer1Role)
|--TicketsForCust1 (Customer1Role, TicketViewerRole)
|--ChangeRequestsForCust1 (Customer1Role, ChangeRequestRole)
Customer2 (Customer2Role)
|--TicketsForCust2 (Customer2Role, TicketViewerRole)
|--ChangeRequestsForCust2 (Customer2Role, ChangeRequestRole)
Burning question:
How do we prevent user Cust2_HighLevelUser from seeing ChangeRequestsForCust1?
EPiServer only checks if any role is sufficient for granting access, and since the user belongs to ChangeRequestRole, they will be granted access, regardless of the customer specific role. Is it possible to make EPiServer check BOTH the customer role, and the page role?
Or do I have to look at this from another view? Please let me know if you have run into this and solved it in another way.
Sorry, long post, but hopefully I get my point across.

There is no Deny flag in the access rights model so you need to code it yourself with that role structure.
Add code to your template base class that denies access and for the PageTree control you can do something like this:
protected void NavSubPageTreeFilter(object sender, EPiServer.Filters.FilterEventArgs e)
{
for (int i = e.Pages.Count - 1; i > -1; i--)
{
PageData pd = e.Pages[i];
if (yourUser.IsInRole("blabla") && ... etc)
{
e.Pages.RemoveAt(i);
}
}
}

Related

RBAC with Ory Keto and ownership of objects

I'm trying to achieve the following points with Ory Keto:
UserA has ownership of ProductA -> Ownership gives one CRUD rights.
UserB has the role Admin -> Admin gives one CRUD rights on everything.
UserA has a role KYCVerified or is part of a group named KYCVerified -> This gives the user additional permissions.
Point 1 describes the concept of ownership, which is described as one of the shortcomings of RBAC (source) with the current state of Ory Keto:
There is no concept of ownership: Dan is the author of article "Hello
World" and is thus allowed to update it.
Point 2 describes a role that basically passes the ownership check, since this role can do everything. This should be possible in the current state, but not in combination with point 1.
Point 3 describes basically the same thing as point 2, but this is more specific to my use case.
I've read this article on the Ory website and this article. However, I'm still unable to wrap my head around this concept. I've made the following example of how I see the concept of ownership with Ory Keto:
# Tenant TenantA needs to be a owner of product ProductA in order to view it
products:ProductA#view#(tenants:TenantA#owner)
# Tenant A is a owner of ProductA
tenants:ProductA#owner#TenantA
But this will result in a lot of rules and I'm not even sure if this is the way to go.
As of this moment you are right. You have to create a bunch of tuples manually. The full set of tuples should be something like:
products:ProductA#owner#UserA
products:ProductA#crud#(products:ProductA#owner)
roles:admin#member#UserB
products:ProductA#curd#(roles:admin#member)
products:ProductA#additional_permissions#(roles:KYCVerified#member)
roles:KYCVerified#member#UserA
With https://github.com/ory/keto/pull/877 you will be able to define global rewrites. It would looks similar to:
import { Context, Namespace } from #ory/keto-config
/**
* "User" is a namespace with no additional rewrite rules defined.
*/
class User implements Namespace {}
/**
* "Role"s only have members.
*/
class Role implements Namespace {
related: {
members: User[]
}
}
/**
* "Product" is a namespace representing a product. It has some rewrites.
*/
class Product implements Namespace {
// Relations are defined and type-annotated.
related: {
/**
* "owners" are the users that are the owners of the product.
*/
owners: User[]
/**
* "admins" are the roles that are administrators of this product (potentially only one).
*/
admins: Role[]
/**
* "special_roles" are the roles a user has to be member of to gain "additional_permissions"
*/
special_roles: Role[]
}
permits = {
// this is probably three/four rewrites (create, read, update, delete) with similar rules
crud: (ctx: Context): boolean =>
this.related.owners.includes(ctx.subject) ||
this.related.admins.some((admin) => admin.related.members.includes(ctx.subject)),
// for the additional_permissions one has to have curd and be member of a special role
additional_permissions: (ctx: Context): boolean =>
this.permits.crud(ctx) &&
this.related.special_roles.some((role) => role.related.members.includes(ctx.subject))
}
}
With that you have to create these tuples:
products:ProductA#owners#UserA
roles:admin#members#UserB
roles:KYCVerified#members#UserA
products:ProductA#admins#(roles:admin)
products:ProductA#additional_permissions#(roles:KYCVerified)
Please note that it is not possible (and not planned right now) to define a single admin group that would have access to everything. You always have to have some kind of relation between the object and subject to query/rewrite it. That is the reason for having the admins and special_roles relations.

ASP.NET Core Identity - NormalizedUserName, NormalizedEmail

While developing a multi-tenant app with ASP.NET Core I noticed that it brings 2 new indices: NormalizedUserName & NormalizedEmail.
The main problem is that it gets too difficult to have a unique user per tenant.
What I mean is having multiple users with the same UserName & Email but different TenantID.
In order to achieve this I have to remove those indices
public static void RemoveIndexes(this ModelBuilder modelBuilder)
{
modelBuilder.Entity<ApplicationUser>(entity =>
{
var normalizedUserNameIndex = entity.HasIndex(u => new { u.NormalizedUserName }).Metadata;
entity.Metadata.RemoveIndex(normalizedUserNameIndex.Properties);
var normalizedEmailIndex = entity.HasIndex(u => new { u.NormalizedEmail }).Metadata;
entity.Metadata.RemoveIndex(normalizedEmailIndex.Properties);
});
}
My questions are:
What is the purpose of these 2 new indices?
What would it affect if we just remove them?
Is there anything we need to pay close attention to after removing them? (e.g. overriding default UserManager functionality or something to that effect)
First of all, I wouldn't change anything of the Identity Framework if I can't oversee the effects. If you insist, you can test what happens yourself. But, do you need to remove the fields?
If the relation of user-to-tenant is one-to-many, then tenantId should not be a field of ApplicationUser but rather be stored in a seperate table, like UserClaims.
You can add multiple tenantId's as claim of the same type, like http://tenant.company.com/Id. It will then become a collection of values, like what happens with roles.
If you don't want this then you can use different claimtypes, like http://tenant.company1.com/Id, http://tenant.company2.com/Id or something like that.
You can choose to include only claims that are linked to the tenant, which could be determined from the site binding or the url, for instance.
This design allows the user to login using the same password everywhere. Please note, this is about identity: who is the user? The user doesn't need to have a different password for every tenant.
It also makes it easier to change a password. Because I wonder, how does your scenario look like with multiple user records for each tenant? Will you update all records at once when a password changes? And how will you support 'forgot password' and other features?

how to make better doc/user -specific write permissions in couchDB?

I am playing around with CouchDB and PouchDB for a project where users have their own database and can add each other to view, or edit their docs.
Goal is to have different levels of accessibility: Depending on the docs themselves other users, who are not the docs.owner will have limited writing/updating permissions. The databases owner/admin grants those privileges.
I am not sure how to properly implement that.
At the moment my solution for this is to let the DBs owner "befriend" other users and add them as members to db/_security, while limiting the writing rights with a _design document like described in: https://github.com/pouchdb-community/pouchdb-authentication/blob/master/docs/recipes.md
But I need a mixture of user specific and DBs specific permissions. So my strategy is to also let the user/owner add special roles besides the default "members" and "admins" to db/_security.
Example:
A user paula owns the DB paulas_DB and wants to grant user jan the right to change the property "location" of every document.
So Paula adds jan to members.names in _security and adds a new list to _security called "movers":
curl -X PUT $HOST/paulas_DB/_security -d '{"members":{"names":["admin","paula","jan"],"roles":[]},"admins":{"names":["admin","paula"]},"movers":["jan"]}'
the docs in paulas_DB are structured like this:
{
"_id": "y",
"_rev": "7-x",
"owner": "paula",
"location": "somewhere",
"name":"thing"
}
now there there is a design document in place in her database, checking that anyone who wants to change the document in general is at least a member AND then checking if they want to change location like this:
function (newDoc, oldDoc, userCtx, secObj) {
// only admins owners or friends of the owner (aka users in the _security members.names list) can edit
if (userCtx.roles.indexOf('_admin') === -1 && oldDoc.owner !== userCtx.name && secObj.members.names.indexOf(userCtx.name) === -1)
{
// next step: add special fields to be either editable or not
throw({forbidden : "sorry. you are not the owner of this document"});
}
// only owners users who are also listed within _security.movers can change the location
if (oldDoc.location !== newDoc.location && oldDoc.owner !== userCtx.name && secObj.movers.indexOf(userCtx.name) === -1)
{
throw({forbidden : "you are not allowed to change the location of an item, dummie!"})
}
}
This method seems to work and was fairly straight forward, but something feels off to add non-standard properties into _security.
Is there another, propper way to do the same thing? Or is that an acceptable design for a document/user specific permission system?

When designing classes, how do you deal with sub-types?

I'm learning about object oriented design as I go through my first real, albeit personal, project.
I'm at the stage where I'm going to build a user object. I have 4 user subtypes:
root-admin - webmaster, site owner
group-admin - leader of a group that has a membership and page (e.g. band manager)
group-member - user that can view, post, and comment as group member on the page and see private content (e.g. band member)
unassociated user - member of the entire site that can view and comment publicly (e.g. fan)
All group admins will be group members and all group members will be unassociated users as well. So basically it's a simple user permissions hierarchy.
With that said, how do I go about setting up the architecture of my class with respect to these levels? Is there a nesting technique? I'm just struggling to hash out the scaffolding here. I have my use cases and narratives written out already.
So you're implementing role-based access control. Each user has one of four roles. So a user is an object and the users's role is one of the attributes in a user object.
enum roles {
root_admin,
group_admin,
group_member,
unassociated_user
}
class user {
string id;
roles role;
}
Next, you need to enforce the business rules that allow users of certain roles to do certain activities.
Start with a simple solution (this is always a good policy):
enum activities {
view,
post,
add_comment
}
And implement a function or class whose job is to say if an activity is allowed for a given role.
class role_based_access_control {
private:
permissions perm;
public:
bool is_permitted(activities a, roles r) {
return perm[r].contain(a);
}
}
Then, in places in your code where you implement different activities, include a call to this function:
void add_comment_to_page(user u, comment c, page p) {
if (!role_base_access_control.is_permitted(add_comment,u.role))
throw "access_forbidden";
...
}
The important thing is to keep the role based access control rules centralized so that they are visible, easy to monitor, and to audit.

Authentication in liferay pages

We are having a portlet on a liferay page. We want to put up up a permission on every action method that is performed. For example on page A we have landed an XYZ portlet. Now we want that whenever there is any action performed form this portlet, we want to check that if the user is having a role to perform this action or not.
It wont be a good approach to put up the code in Action method of the portlet cause we are having approximately 20 such pages and portlets.
Can we have some sort of filter or so, so that each action request is checked if the user is having the access to the content or not.
Thank you...
My idea.
Use a filter to intercept all request
You can add a filter to the Liferay Servlet to check every request.
For that you can use a hook-plugin.
Look at this :
http://www.liferay.com/fr/documentation/liferay-portal/6.1/development/-/ai/other-hooks
http://connect-sam.com/2012/06/creating-servlet-filter-hook-in-liferay-6-1-to-restrict-access-based-on-ip-location/
Issue with filter is that you can't access ThemeDisplay or use PortalUtil.getUser(request).
So you must use work around like that :
private User _getUser(HttpServletRequest request) throws Exception {
HttpSession session = request.getSession();
User user = PortalUtil.getUser(request);
if (user != null) {
return user;
}
String userIdString = (String) session.getAttribute("j_username");
String password = (String) session.getAttribute("j_password");
if ((userIdString != null) && (password != null)) {
long userId = GetterUtil.getLong(userIdString);
user = UserLocalServiceUtil.getUser(userId);
}
return user;
}
Filtering the request
To filter the request you must get :
page id (Layout id in Liferay)
portlet id
portlet lifecycle
One more time using a filter is a pain because you can get the ThemeDisplay. These params are easy to get (with real object instancee) with ThemeDisplay.
So you must get this as parameter in the request.
final String portletId = ParamUtil.get((HttpServletRequest) servletRequest, "p_p_id", "");
final String layoutId = ParamUtil.get((HttpServletRequest) servletRequest, "plid", "");
final String portletLifecycle = ParamUtil.get((HttpServletRequest) servletRequest, "p_p_lifecycle", "");
Lifecycle details :
portletLifecycle is a int and the meaning of value is :
0 : RENDER
1 : ACTION (the one that interests you)
2 : RESOURCE
I think that with this data you can be able to define if user can or cannot make the action.
You can get user roles from the user.
You can get the current page and portlet linked to the request.
And you can know if the request is an action request.
Good luck with Liferay.
You can add freely configurable permissions to Liferay, see the Developer Guide for detailed information. My first guess on this would be that these affect "model resources", e.g. the data that your portlet is dealing with, rather than portlet-resources, e.g. permissions on the individual portlet itself. Think of portlet-permissions as permissions that are defined by Liferay, model-resources as permissions where you can come up with your own vocabulary on the actions, e.g. "UPDATE_ADDRESS" etc.
These permissions will typically be tied to roles, which are granted to users/usergroups/etc.
Based on this variability, it depends on the nature of your permissions if you can write a filter to generically check permissions, or if it depends on more than the individual action call.
If you determine that there is a generic solution, look up PortletFilters, they behave just like ServletFilters. These can easily provide a home for permission checks.
It's quite hard to cover this topic in such a short answer, I hope to have given enough resources for you to continue your quest.
You can abuse some existing portlet permission like "Add to Page" and set it to roles that should call the action.
And by the rendering and action phases validate "has the user necessary permission".
Or you can create new permission and configure it by portlet-configuration. This way is cleaner, but difficulty.