How preventing editing a node when this node has a specific workflow status - drupal-7

I use Drupal 7 and I would like to prevent the edition of some nodes for specific user roles when the node has a specific workflow status.
For instance, if the node has the workflow status "validated", the user having the role "contributor" is not able to edit it but the user with the role "validator" well.
If the workflow status is "to validate", both user roles can edit it.
Is it possible to implement this kind of process with the modules "Rules" and "Workflow"?

You probably want to use hook_node_access($node, $op, $account). Create this hook in one of your modules. To potentially deny access on edit first add a check for $op == "update" then add your validation logic. To deny access return NODE_ACCESS_DENY otherwise return nothing or NODE_ACCESS_IGNORE.
A simple example would be as follows:
function MYMODULE_node_access($node, $op, $account) {
if (($op == "update") && ($node->type == "node_type_to_check")) {
// Add your extra validation checks here
// ...and if you then want to block access return the following
return NODE_ACCESS_DENY;
}
}
You will find more information about hook_node_access() in the Drupal 7 API docs.
WARNING: If you are logged in as the admin user (user 1) then you will be able to edit the node regardless of your hook_node_access() rules, so make sure that you test this with a "normal" user.

Related

Can I make a collection append-only in Cloud Firestore?

I want to write game events and audit logs from my app to Cloud Firestore. Once written, I don't want the user to be able to modify or delete these events/logs.
How can I do this?
Rules in Cloud Firestore makes it quite simply to make a collection, or even the entire database, into an append-only system from the mobile & web clients.
Example
Below is a set of rules that will turn the root collection audit_logs into an append-only collection.
service cloud.firestore {
match /databases/{database}/documents/ {
function permission_granted() {
return request.auth != null; // Change this to your logic.
}
match /audit_logs/{log} {
allow update,delete: if false;
allow read, create, list: if permission_granted();
}
}
}
Let's break down the most important pieces.
Function: permission_granted()
function permission_granted() {
return request.auth != null; // Change this to your logic.
}
This one is just a placeholder for however you want to restrict insert new documents or reading existing documents in the collection. In this case it's letting anyone who has signed in using Firebase Auth -> You might want it more restrictive.
It just returns true or false, which we'll use later to actually enforce.
Match: Root collection audit_log
match /audit_logs/{log} {
...
}
This one's simple, we're just matching against any requests regarding for the root collect called audit_logs. The document Id in questions is made available via $(log) due to the {log} piece.
Blocking any modification that is append-only
allow update,delete: if false;
The 2 write methods that are not append-only are update and delete, so here we just universally disallow any mobile & web SDK from performing them.
Allow the rest
allow read, create, list: if permission_granted();
Lastly, using the permission_granted function we set up earlier, we allow reading, listing, and creating new documents in the collection.

Securing system-generated nodes in firebase

I've been going through the rules guide but haven't found an answer to this.
App users are able to submit "scores" of different types, which are then processed in JS and written to a "ranking" node. I have it set up so that every time a new score is submitted, the rankings are automatically recalculated and a new child is written if the user doesn't exist or updated if the user exists.
My question is how to secure this "ranking" node. Everyone should be able to read it, nobody except the system should be able to write it. This would prevent people from submitting their own rankings and aggregate scores.
EDIT
This is the operation:
Ref.child('rankings').child(uid).once('value', function (snapshot) {
if (snapshot.exists()) {
snapshot.ref().update(user); //user object created upstream
} else {
var payload = {};
payload[uid] = user;
snapshot.ref().parent().update(payload);
}
});
How would I add custom authentication to this call? Also, since I'm using AngularJS, is there any way to hide this custom token or would I have to route it through a backend server?
The key part of your problem definition is:
only the system should be able to write it.
This requires that you are able to recognize "the system" in your security rules. Since Firebase security is user-based, you'll have to make your "system" into a user. You can do this by either recording the uid from a regular user account or by minting a custom token for your "system".
Once you have that, the security for your ranking node becomes:
".read": true,
".write": "auth.uid == 'thesystem'"
In the above I assume you mint a custom token and specify thesystem as the uid.

How to grant user User Points after publishing content, while content's default is unpublished?

I'd like to grant users User Points when their added content of the content type 'Content', which is unpublished by default, is published. I tried this with the Rules module: - Event: After saving new content - Elements: * Content is of type: Content * Content is published - Actions: Grant points to a user
So, when a user adds 'Content' content, they don't get any User Points and their content is unpublished. My idea was that they'd be granted the points after I - as moderator - had published their content. But apparently, they don't get points after I've done that with the rule I set up. What did I do wrong and how can I solve this?
Please check the flag module as my friend told to
STEP1- make a flag module named(Publish node and grant points to user)
and after that make a event in
STEP2- Configuration--> workflow-->rules--> and make an event over there selecting the flag you just made i.e.,(A node has been flagged, under "add points on publish article flag")
after that set action as ( Grant points to a user) Data Selector:flagged-node:author
Points:5
Point Category value:(Select your category)
operation value: flagged-node:author
automatically approved
and you are done now..
when the user post the article it will be unpublished and when admin make it published when the user will click on flag this iten the points will be granted to user.
You could create flag and give flag name as Publish node
and grant points to user
Then add new rule with event A node has been flagged, under
"Publish node and grant points to user"
For actions publish this node and grant points to user use
this data selector: flagged-node:author

CakePHP - Site Offline - Admin Routing Not Working

I setup the following code in my app_controllers.php file to control access to the site when the site is set to OFFLINE (site_status = 0).
function beforeFilter(){
// Site Offline = 0 , Site Online = 1
if($this->Configuration->get_site_status() == 1){
// Allow access to the site to all users and perform all required
// beforeFilter code
}else{
...
// If site is OFFLINE but User is logged in allow access.
// Later I will need to change it to only allow admin access if logged in as I am still developing
// Everyone else will be denied access even if they are able to authenticate
if(!$this->Auth->user() == null){
$this->layout = 'default';
$this->Auth->allow('*');
}else{
$this->layout = 'offline';
$this->Auth->deny('*');
}
...
}
}
Everything works great when the requested address looks like the following:
http://www.mydomain.com/articles
However, when I have the following it does not work properly
http://www.mydomain.com/admin/articles
It prevents access to the site correctly, but it fails to use the $this->layout = 'offline'. It defaults back to the default layout.
What do I need to do to fix this.
Thank you!
Your if conditions look weird. They are:
If site is offline and user logged in
use default layout
otherwise
use offline layout and require authentication on all pages
I.e. you're using offline layout when site is online OR user is not logged in. Are you sure that is what you want?
Well, the first thing that looks out of place to me is:
(!$this->Auth->user() == null)
This looks very wrong and might be causing your problems. I would suggest changing this to something like:
(!is_null($this->Auth->user())
or
($this->Auth->user() !== NULL)
Edits
First, check out the PHP logical operators. You were appending a NOT statement to the return value of $this->Auth->user(). So, with a user logged in you're essentially asking if false is equal to null, which of course it isn't and never will be.
Second, check out the PHP comparison operators. You aren't wanting to check if the value of $this->Auth->user() is equal to the value null, you're wanting to check if the data type of $this->Auth->user() is equal to the type null. In short, null is a data type, not a value. If you did just have to use "=" in your if statement then you would want to use the identical === check or the identical not check !==.

How to add acl per row?

I have problem with ACL. I read tutorial from here and here, and now I know how to add permission to some user/group to edit his profile, but then all users can edit each other profile. How I can set permission so user can edit just his own profile, not others, or can I somehow put this code in edit function:
function edit($id = null) {
if ($logedUserId != $id) {
// deny access
return;
}
// edit user
}
Assuming that you are using action-based access control (which it appears as though you are), then unless you have an action named after each profile (which would be completely wrong) you will have to do an additional check within the edit() method to ensure that the profile being edited belongs to the currently logged in user.
So you sort of answered your own question--correctly.

Resources