When do we explicitly set CME permissions given OrganizationalItemData attributes? - tridion

I'm creating a list of permission settings for an OrganizationalItemData object with the Core Service and am looking for help confirming when to explicitly set permissions in the CME.
Question
Given an organizational item and knowing its shared, localized, and permissions inheritance settings, when do we need to explicitly set permissions for it (in the CME under Properties > Security)?
API Details
Given OrganizationalItemData orgItem I can check the following (with details from the documentation).
IsShared
Gets or sets whether the item is shared in the current context Publication.
IsLocalized
Gets or sets whether the item is localized in the current context Publication.
IsPermissionsInheritanceRoot
true if permissions are specified for item itself; otherwise, false, if item inherits permissions from the parent item.
Example Code
I can use the Core Service to check permission settings in a given CMS. For example:
var CreatedHere = !orgItem.BluePrintInfo.IsShared.GetValueOrDefault();
var IsLocalized = orgItem.BluePrintInfo.IsLocalized.GetValueOrDefault();
var PermissionsFromParent = !orgItem.IsPermissionsInheritanceRoot.GetValueOrDefault();
I'd like to output the permissions for a given Organizational Item, but also instructions for when the CMS administrator actually needs to set properties in its publication. For example I could use a script against CMS Test to show what CMS Production might need.
Scenarios
Here's the truth of it (I think).
Shared Local(ized) PermissionsRoot Set here?
No No No No, set in parent in this Publication.
No No Yes Yes, set in this "local" item.
No Yes No Not possible. Can't localize non-shared item.
No Yes Yes Not possible. Can't localize non-shared item.
Yes No No No, set in parent item in higher Publication.
Yes No Yes No, set in this item but in a higher publication.
Yes Yes No No, set in parent item in this Publication.
Yes Yes Yes Yes
I might be wrong in my understanding of these options, but so far it seems we only need to explicitly set permissions for two scenarios:
For a non-shared (local) item that is a permissions root (Inherit Security Settings from Parent unchecked)
For a shared, localized item that is a permissions root.
Do I have the right scenarios and/or am I missing something in the 2^3 scenarios above?

I think you have it about right Alvin.
First of all, the only place you can ever have permissions is at an inheritance root.
Secondly, normal blueprinting rules apply.
I used to draw this as a diagram with a zig-zag arrow, showing the search for permissions:
(Imagine you're a folder trying to figure out stuff)
Am I a local inheritance root? No - keep on looking
OK - so BluePrinting applies, so maybe I'm a shared folder and I'm an inheritance root via bluePrinting? Nope? OK - keep on looking,
So what about that folder above me in the tree? Are there any permissions there? Is it a local inheritance root? Is it an inheritance root by way of BluePrinting? Nope - keep on looking
And so on all the way to the top
So looking at your table: Anything where "Permissions root" is false, can't have it's own permissions, so correctly you have either no or not possible for each of these.
For org items that are inheritance roots, the next question is are they local/localized or are they shared. Again you seem to have got this correct.
There's only one point of attention that I'd mention, and that is that perhaps "Localized" is the wrong name for your second column. I'd say "local" instead. An item is local if it is not shared, and this can be because it's been localised from a shared item in another publication, or because it's been created locally in this publication. I think you realise that any scripted or programmed approach you take needs to accommodate both scenarios.

Related

How to detect any changes performed on any object (OU, Container, Computers or Users) in an Active Directory?

Suppose there is an active directory, how to check or detect for any changes like add, delete, modify or even moving an object from one OU or container to another.
Edit : Is there any way to perform this using the LDAP Queries ?
There are 3 documented ways to track AD changes: https://msdn.microsoft.com/en-us/library/ms677625(v=vs.85).aspx

avoid adding a folderish object inside another object of the same type

We have a content type (lets call it, MyFolder) that could contain any other content type registered in the site except for MyFolder itself.
what's the simplest way to accomplish this?
registering a new workflow and setting guards?
removing the Add MyFolder permision of the content type from the object with an event at creation time?
filtering the content type from the list of addable content types in the object? how?
none of the above? how then?
I guess point 3 should work fine, set the "Filter content types" flag on MyFolder and select all others that should be addable in "Allowed content types" of MyFolder
Of course if you add other content types, you have to manually extend the "Allowed content types" of MyFolder.
Another solution could be to set the localallowedtypes on every instance of MyFolderusing an eventhandler zope.lifecycleevent.interfaces.IObjectCreatedEvent.
In this event handler you can check for the parent and set the local allowed types.
Then you have to make sure, that your "normal" user cannot modify the local allowed types by uncheck the Modify constrain types permission.
But also here you have to manage new content types manually.
Yet another solution:
If your are using dexterity types, there is a TypeSettingsAdapter (https://github.com/plone/plone.app.dexterity/blob/master/plone/app/dexterity/browser/types.py)
You can register your own adapter for your specific type.
I think than point 3 has some complexity and turnarounds because you need to revoke Modify contrains types permission but maybe that is not the intended behavior because that way you are restricting user to modify all the locally allowed types besides MyFolder, and finally the user could find workarounds for this limitation... for example, MyFolder -> Folder -> MyFolder
point 2 is more simple, you just revoke one permission with an event handler when a new MyFolder is created, since you are revoking the permission and disabling acquisition for that perm. there is no easy workarounds to avoid the limitation, and the user could still use the configuration of locally allowed types for every addable content type but MyFolder.

Do not allow a user to delete a node but allow to delete through Views Bulk Operations

I have the following scenario:
Editor Role should not be allowed to
delete nodes. Therefore the corresponding
permission is de-selected in the
permissions page.
However Editor
should be able to to delete nodes
from Views Bulk operations. Using
Rules an action is created called
"safe delete" that checks things like
if the node is not published etc.
before deleting the node.
The problem is the Views Bulk Operations respects Node permissions. Editor will not be able to delete the node as he has not been given that permission. Is there a way that Editor can become a higher role user (as sort of sudo) while performing that action in VBO? Alternatively is there a way to tell VBO to ignore node access for this action?
I'm sure this is a mainstream requirement but I can't seem to find a solution.
Solutions which do not involve programming will be preferred.
The simple, but not-so-clean way, is the route you already took, but with an additional, small module to help it.
has a function my_module_can_delete($user), that returns TRUE if the user is allowed to delete, FALSE if the user is not.
implements hook_form_alter() to modify and delete the button on the node_edit form, if my_module_can_delete($user)
implements hook_form_alter() to modify the confirm form that is called on /node/%nid/delete, and add a message there, telling the user he or she my_module_can_delete($user). This should be enough, since disabling this form will result in users not being able to get past this form. FORM-API will take care of that.
However, you can make it more sturdy, to catch other deleting modules:
implements hook_nodeapi(), $op == 'delete' to catch delete actions and halt (by invoking drupal_goto(), or calling drupal_access_denied() to enforce a user-error. Only catch delete-actions if the referer was the delete-confirm-form as mentioned above. Or, more secure, whitelist your VBO-action and return false on all other referers. A referer can often be found by reading out the $node passed along to hook_nodeapi().
A, IMHO, much cleaner, but probably more intensive alternative, would be to simply make sure your batches/actions are called on every delete action.
In a module, you could do this by avoiding all the VBO-configuration and leaving all the extra-delete actions out of there.
Then write a module that implements hook_nodeapi() and then calls all the cleaning actions from there. That way you can be sure that your delete-actions are called on every delete-action on any node. Obviously you can add some conditions into your hook_nodeapi() to only invoke your modules in certain cases (node-types, user-roles, permissions and so on).
Well, it seems to me that you've got a setup where you don't want Editor Role users to delete things, really, except in certain extreme situations. Here's my suggestion:
1) Install Flag module. Create a 'To Be Deleted' flag that can only be assigned by Editor Role people.
2) I haven't looked into it, but I"m sure there's probably a rule or trigger/action combo which will unpublish the node when the 'To Be Deleted' flag is assigned to it. This will remove the node from casual view.
3) Then either set up some cron run activity (trigger/action or rule) to delete nodes with 'To Be Deleted' flag set on them, or have another user with higher permissions come in occasionally and delete out the flagged items.
This way you're not actually bypassing the permissions system, and yet things are still being removed from your site.
I got caught out of this for a while until I noticed the "actions_permissions" module, enable this and on the Permissions page you can provide access to specific actions on a role by role basis.
I don't have a good no-coding solution, and I'm not sure I would call this solution "great" - but one way might be to implement a simple module with a form_alter hook that removes the delete button from the node edit forms as they are built.
In general it seems like the role either has permission to delete nodes or not, and monkeying around like this is going to be less robust that you might like.

Best Practices for controlling access to form fields

I have a classic 3-tier ASP.Net 3.5 web application with forms that display business objects and allow them to be edited. Controls on the form correspond to a property of the underlying business object. The user will have read/write, readonly, or no access to the various controls depending on his/her role. Very conventional stuff.
My question is: what is the object-oriented best practice for coding this? Is there anything more elegant than wrapping each control in a test for the user's role and setting its Visible and Enabled properties?
Thanks
You'll want to drive this off of data, trust me. You'll need a lot of tables to do it right, but it is so worth it in the end. Having to crack open code and edit a bunch of if-statements every time the business wants to change permissions is a killer.
You'll want a table for your main high-level types, things you probably already have business object clases for. Then a table for each status of them. Then a table for the fields of these classes. Then a table for user roles (admin, guest, etc.) Finally a table for the permissions themselves. This table will have columns for business class, status, field, user role, and then what permission they have. For permissions I would go with one field and use an enum: Hidden, ReadOnly, Editable, and Required. Required implies Editable. Anything but Hidden implies Visible. Finally put a Priority column on this table to control which permission is used when more than one might apply.
You fill out this table with various combinations of class, status, field, role, and permission. If a value is null then it applies to all possible values. So you don't need a trillion rows to cover all your bases. For example, 99% of the time, Guest users are read-only users. So you can put a single entry in the table with only the Guest role specified, everything else is null, and set it's Priority nice and high, and set the permission to Read Only. Now for all classes, all statuses, all fields, if the user is a Guest, they will have Read Only permission.
I added status to your list of concerns because in my experience, business all the time wants to constrain things by an object's status. So maybe users can edit an item's name while it is in Draft status, for example, but once it is in Posted status, the name is no longer editable. That is really common in my experience.
You'd want to bring this table into memory and store it in the app's cache, because it's not going to change very often, if ever, unless you do a whole new version.
Now the above is going to handle 90% of your needs, I suspect.
One area that will have to be handled in code, unless you want to get really fancy, is the cases where a user's permission is determined in part by the value of fields in the object itself. So say you have a Project class, which has a Project Manager class. Now the Percent Complete field of the class is basically read-only for everybody, except the Project Manager. How are you going to handle that? You'll need to provide a way to incorporate specific instances of a class into the decision making process. I do this in code.
To work properly, I have found that access levels should be in this increasing order:
NONE, VIEW, REQUIRED, EDIT.
Note that REQUIRED is NOT the top level as you may think it would be since EDIT (both populate & de-populate permission) is a greater privilege than REQUIRED (populate-only permission).
The enum would look like this:
/** NO permissions.
* Presentation: "hidden"
* Database: "no access"
*/
NONE(0),
/** VIEW permissions.
* Presentation: "read-only"
* Database: "read access"
*/
VIEW(1),
/** VIEW and POPULATE permissions.
* Presentation: "required/highlighted"
* Database: "non-null"
*/
REQUIRED(2),
/** VIEW, POPULATE, and DEPOPULATE permissions.
* Presentation: "editable"
* Database: "nullable"
*/
EDIT(3);
From the bottom layer (database constraints), create a map of fields-to-access. This map then gets updated (further restrained) at the next layer up (business rules + user permissions). Finally, the top layer (presentation rules) can then further restrain the map again if desired.
Important: The map must be wrapped so that it only allows access to be decreased with any subsequent update. Updates which attempt to increase access should just be ignored without triggering any error. This is because it should act like a voting system on what the access should look like. In essence, the subsequent layering of access levels as mentioned above can happen in any order since it will result in an access-level low-water-mark for each field once all layers have voted.
Ramifications:
1) The presentation layer CAN hide a field (set access to NONE) for a database-specified read-only (VIEW) field.
2) The presentation layer CANNOT display a field when the business rules say that the user does not have at least VIEW access.
3) The presentation layer CANNOT move a field's access up to "editable" (nullable) if the database says it's only "required" (non-nullable).
Note: The presentation layer should be made (custom display tags) to render the fields by reading the access map without the need for any "if" statements.
The same access map that is used for setting up the display can also be using during the submit validations. A generic validator can be written to read any form and its access map to ensure that all the rules have been followed.
I have often found that this is really the only real easy and understandable way to do it, as your interface needs to modify based on the information and level of editing that they can complete.
I do find typically though that depending on the needs, you can interject the "cannot edit" information by passing role information to the business level if you have plans to move to different presentation levels. but this adds complexity, and if you are only building for one interface it would most likely be overkill
For the website menus we can have different menus based on users role by using the Sitemaps. For controls like Buttons we will have to hide them using their Visible property. I think a good idea will be to create a server control (Button) and expose the Role property. This will hide the Button if the user is not in the correct role.
My first instinct for doing this in a more OO way would be to handle your roles and their implementations for this purpose (control permissions read/write/etc) is to use the abstract factory pattern for your roles. I will be happy to explain the ins and outs of what I am talking about if you'd like but there are probably 900 examples on the web. Here is one link (disclaimer: it's my blog but it does happen to talk to using abstract factory for roles specifically)
Using something like this you could then use a number of methods to display the correct controls for each of your business object properties with the correct attributes (read/write/hidden/displayed/etc).

Best practices for control permissions?

Hey all, I need some advice on this...
We have certain permissions setup in the database for certain levels of control a user can have over the application. Disabled, ReadOnly and Edit.
My question is: Are there more generic/better ways to handle permissions applied to a form element on the page than writing a security method/check per page to enable/disable/hide/show proper controls depending on the permissions allowed?
Anyone have any experience handling this in different ways?
Edit:
I just thought about the possibility of adding constants for each layer that needs security and then adding an IsAuthorized function in the user class that would accept a constant from the form that the control is on, and return boolean to enable/disable controls, this would really reduce the amount of places I'd have to hit when/if I ever need to modify the security for all forms.
Cheers!
Sorry for going slightly off-topic here, but learn from my mistake:
I had a simple web app one time that I was developing and I thought that I'd setup 3 levels of security: limited read-only (public), read-limited write (user), read-write (admin). The users table had a level of security in it and everything worked fine... until I needed finer control over security levels as the project grew. It all started with a user that needed more than user control in one area of the program but not full admin control.
What I should have done was setup an expandable system with finer control even though I didn't need it at first. This would have saved me sooo much time.
I think there are more possibilities than you are considering.
Hidden/Visible - is the field visible or not
Blanked/System/Unchanged - does the system initially set the value to blank, or to some business-rule-provided value, or is it left as-is
ReadOnly/Editable - can the user change the value
Required/LeaveBlank/Optional - is the field required to not be blank, or can it be left blank assuming it was blank to begin with, or is it optional and can be blank in any case
Also you need to consider a lot of factors that go into making the decision
Role - usually the user has 1 or more roles, and those roles can allow or disallow different possibilities
Status - the status of the object in question often controls which fields are editable, regardless of role
Object - often the values of the object itself determine what is allowed when, beyond just's it's status
Task - you might break down editing things into more than just read and edit
Section - I often want to control the settings for an entire section of the object or screen, and all the controls inherit those settings, but can override them on an individual basis if needed
Implementation?
First, make sure you have a clear vision of how the page lifecycle is handled. Mine usually goes something like this.
Get the object they are editing, usually from session state, sometimes if they are doing a "new" operation you may need to create a stub data structure for them to work on
If this is the first time the page is loading up, populate choices into dropdowns, this is usually a generic process, done only once
If this is a postback, update the object being edited by reading in values off the controls
Process events such as button clicks
Run through all your business edits, these should alter the data structure they are editing
Update the visibility and editability of the controls based on all the data you have on hand
Populate the controls with the data from the object being edited, including validation messages, or error messages
You may want to check how django handles forms and validates them. Forms are handled like models, they have their own class, so their field list, validation rules and display logic is no more scattered throughout the view, the controller and the helper. With such a structure, it's pretty clear where the hidden/readonly/editable logic belong.
It seems that such a feature is not yet implemented in rails. It's not only a time-saver, it keeps your code clean and structured. You could start by creating a base class for forms, where validation is separated from the controller.
To work properly, I have found that access levels should be in this increasing order:
NONE, VIEW, REQUIRED, EDIT.
Note that REQUIRED is NOT the top level as you may think it would be since EDIT (both populate & de-populate permission) is a greater privilege than REQUIRED (populate-only permission).
The enum would look like this:
/** NO permissions.
* Presentation: "hidden"
* Database: "no access"
*/
NONE(0),
/** VIEW permissions.
* Presentation: "read-only"
* Database: "read access"
*/
VIEW(1),
/** VIEW and POPULATE permissions.
* Presentation: "required/highlighted"
* Database: "non-null"
*/
REQUIRED(2),
/** VIEW, POPULATE, and DEPOPULATE permissions.
* Presentation: "editable"
* Database: "nullable"
*/
EDIT(3);
From the bottom layer (database constraints), create a map of fields-to-access. This map then gets updated (further restrained) at the next layer up (business rules + user permissions). Finally, the top layer (presentation rules) can then further restrain the map again if desired.
Important: The map must be wrapped so that it only allows access to be decreased with any subsequent update. Updates which attempt to increase access should just be ignored without triggering any error. This is because it should act like a voting system on what the access should look like. In essence, the subsequent layering of access levels as mentioned above can happen in any order since it will result in an access-level low-water-mark for each field once all layers have voted.
Ramifications:
1) The presentation layer CAN hide a field (set access to NONE) for a database-specified read-only (VIEW) field.
2) The presentation layer CANNOT display a field when the business rules say that the user does not have at least VIEW access.
3) The presentation layer CANNOT move a field's access up to "editable" (nullable) if the database says it's only "required" (non-nullable).
Note: The presentation layer should be made (custom display tags) to render the fields by reading the access map without the need for any "if" statements.
The same access map that is used for setting up the display can also be using during the submit validations. A generic validator can be written to read any form and its access map to ensure that all the rules have been followed.
(Also see thread: Best Practices for controlling access to form fields)

Resources