Setting rights in a velocity macro

I’m implementing a publication submission and tracking system for our wiki and I’ve created an API endpoint (using admin account with scripting rights) that gets called from JavaScript when a user changes the value of a dropdown box. The velocity macro in the API endpoint changes a status field in a custom class instance attached to the page, and in the case of submitting a page for publication, it also records the user who made the submission. This part works great for all users.

The problem I’m having is that I want to change the rights on the page once it has been submitted so that the user can’t make any further changes while it goes through our approval process. The code between the “TEST BEGIN” and “TEST END” comments works perfectly when I am using my admin account. However, when using a regular user account in the AuthorGroup, this block of code does nothing, while everything else works fine.

Even though this macro was saved from my admin account, the code that creates and modifies the rights object doesn’t work when a non-admin user triggers it. Is this expected behavior or am I doing something wrong?

{{velocity}}
#if ($stringtool.contains("$!request.getRequestURL()", "/get/") && ("$!request.page" != ""))
  #set ($page = $xwiki.getDocument("$request.page"))
  #set ($status = "$!request.status")
  #set ($obj = $page.getObject('xwiki:assets.classes.PageMetadata'))
  #if ($obj)
    #set ($_ = $obj.set("status", "$status"))
    #if ($status == "sub")
      #set ($_ = $obj.set("requester", "$!request.userRef"))
      ## TEST BEGIN #################################
      #set ($_ = $page.removeObjects('XWiki.XWikiRights'))
      #set ($rights = $page.newObject('XWiki.XWikiRights'))
      #set ($_ = $rights.set("groups", "XWiki.AuthorGroup"))
      #set ($_ = $rights.set("levels", "edit,delete"))
      #set ($_ = $rights.set("users", ""))
      #set ($_ = $rights.set("allow", 0))
    #elseif ($status == "wip")
      #set ($_ = $page.removeObjects('XWiki.XWikiRights'))
      ## TEST END ###################################
    #end
    #set ($_ = $page.save("Changed status to $status"))
    #rawResponse('OK', 'text/plain')
  #else
    #rawResponse('FAIL', 'text/plain')
  #end
#end
{{/velocity}}

Are you sure this is where the problem is ? I don’t see anything in those lines that would be impacted by the current user (your test is also a bit strange, since it’s impossible to have both TEST BEGIN and TEST END be printed).

Howether getDocument is relying on current user rights, so if the current user does not have the right to read that page, getDocument will return null. You can use getDocumentAsAuthor instead of rely on script author right. Similarly, save won’t work if the current user does not have edit right on the page, in which case you would have to use either saveWithProgrammingRights or saveAsAuthor depending on if you want to claim that the change was made by the current user or by the author of the script.

@tmortagne, thanks for your reply, but I think you might misunderstand what I’m doing. The TEST BEGIN and TEST END are just comments - I’m not trying to print them. I just put them there to indicate the problematic code.

I know that this code is the problem because when I run this as my admin user, everything works, even the code between the TEST BEGIN / TEST END. The fields (status and requester) of the custom object attached to the page are correctly modified and the permissions are changed. I saved this code as my admin user (with scripting rights), expecting it to work for everyone.

When I log in as a non-admin user without scripting rights, everything works except for the code between TEST BEGIN and TEST END. In other words, the fields (status and requester) on the custom object attached to the page ARE correctly modified, but the permissions of the page are NOT modified.

Since scripts saved by someone with scripting rights can be run by everyone, I would expect this code to work for my non-admin user without scripting rights, but it only partially works. Is there something special about modifying permissions? Why is code for modifying permissions not working in the case of a non-admin user without scripting rights when the rest of the code works correctly?

Perhaps this will be more clear:

{{velocity}}
#if ($stringtool.contains("$!request.getRequestURL()", "/get/") && ("$!request.page" != ""))
  #set ($page = $xwiki.getDocument("$request.page"))
  #set ($status = "$!request.status")
  #set ($obj = $page.getObject('xwiki:assets.classes.PageMetadata'))
  #if ($obj)
    #set ($_ = $obj.set("status", "$status"))
    #if ($status == "sub")
      #set ($_ = $obj.set("requester", "$!request.userRef"))
      ## PROBLEM 1 BEGIN #################################
      #set ($_ = $page.removeObjects('XWiki.XWikiRights'))
      #set ($rights = $page.newObject('XWiki.XWikiRights'))
      #set ($_ = $rights.set("groups", "XWiki.AuthorGroup"))
      #set ($_ = $rights.set("levels", "edit,delete"))
      #set ($_ = $rights.set("users", ""))
      #set ($_ = $rights.set("allow", 0))
      ## PROBLEM 1 END ###################################
    #elseif ($status == "wip")
      ## PROBLEM 2 BEGIN #################################
      #set ($_ = $page.removeObjects('XWiki.XWikiRights'))
      ## PROBLEM 2 END ###################################
    #end
    #set ($_ = $page.save("Changed status to $status"))
    #rawResponse('OK', 'text/plain')
  #else
    #rawResponse('FAIL', 'text/plain')
  #end
#end
{{/velocity}}

Problem 1 and Problem 2 are both trying to change permissions, but one is trying to add them and the other is trying to remove them based on the value of $status. They both work for an admin with scripting rights and both fail for a regular user without scripting rights, even though the code was saved by the admin with scripting rights.

Again, I’m not convinced you identified properly the code which “does not work”. Your problem is probably more with #save and the fact that you are trying to save this change as the current user (which probably does not have the right to do this change) instead of as the script author.

When someone who isn’t the script author runs this, the save works for the status object, but it doesn’t work for the rights object. The status object is always saved correctly no matter who runs the script, but the rights object is only saved when run by the script author. So ultimately my question is whether saving a rights object is more restrictive than saving a custom defined object (my status object in this case)?

From everything I’ve read in the documentation, if a script is saved by someone with scripting rights, then that script may be run by others without scripting rights. This has been the case with every script I have written except for this one. The rights object appears to have additional restrictions I haven’t seen mentioned anywhere (or I just missed it).