Fwd: link / unlink / relink

John Rouillard via Roundup-users <[email protected]> Sat, 16 Mar 2024 23:03:38 -0400
Newsgroups gmane.comp.bug-tracking.roundup.user
Message-ID <CANfx4msL0enT_JcnBkmC=BZztpCcgL-Gc_BGL5X8S04GWA868w@mail.gmail.com>
Hi all:

It appears that my main email site can't send anything to sourceforge lists.
sorry if this becomes a repeat.

Hi r2d2:

In message <[email protected]>,
[email protected] writes:
>I would like to restrict the unlinking (and relinking) of issues /
>messages / files for "user" roles and only allow it for "admin"
>roles.

Adding/removing an item from the messages multilink is audited. So you
will have a record of what happened in any case. Setting the whole
messages multilink will be recorded as well but I am not sure if the
history shows multiple add/remove or a single set in the history. Ralf
any idea?

>Is this possible with some access control rules,

Unlinking can be stopped by an auditor. The idea is (untested and
probably uninterpretable Python like code below):

    audit(db, cl, itemid, newdata)

       # Is the user changing messages
       if 'messages' not in newdata: return

       # Is the user removing or replacing a message?
       current_messages = cl.get(itemid, 'messages')
       new_messages = newdata['messages']
       removed = [message for message in current_messages if message
                  not in new_messages]

       if not removed: return

       # If the user is an Admin hasPermission always passes, but
       # let's say we wanted to add a user who can edit messages
       # if they have the moderator permission.
       #
       # See: https://roundup-tracker.org/docs/customizing.html#restricting-the-list-of-users-that-are-assignable-to-a-task

       if not db.security.hasPermission('moderator', userid, cl.classname):
        raise ValueError('User %s does not have moderator role, '
                         'and can not remove messages from an issue')

Note that this code doesn't quite do what you think it would:

       if len(newdata['messages'] > len(cl.get(itemid, 'messages')):
          # user made a comment and is adding to the messages (NOT!)
          return

While @remove can only remove, not replace, items in the messages list,
a set/edit operation can. So I could remove item id 10 and replace it
with item id 55 in one transaction. This preserves the length of the
list. This could be done using the Rest interface as well. If I am
wrong, somebody can correct me.

Detecting re-adding is a bit trickier. Adding (by commenting) and
re-adding look kind of identical from the auditor's viewpoint.  There
is no database method to report if a message id was previously assigned
to the issue.  However for new messages/comments the message id being
added will either:

     be missing from the db as the transaction to create the msg and
       update the db isn't committed yet. So

         cl.db.msg(added_message_id, 'creation')

       will raise an exception. I think this is the correct case.

     have its creation date within a second or two of now() and the
       creator will be the current user.

To detect re-adding, you can create a "removed_messages" property and
look to see if your added message:

  added = [message for message in new_messages if message
            not in current_messages]

is in the "removed_messages" property. An auditor would have to keep
the removed_messages property list up to date.

Another way is to grovel through the issue's history and look for
entries that remove/unlink or set (possibly replacing one or more
message ids) the messages property. You would start from the
current values of messages and walk backward through history recording
any unlinked items and calculating any removed items when a set
operation happens.

Can it be done, yes. Is checking for re-adding worth it? Probably not.

If a user tries to add a currently existing message, it won't do
anything as you can't have duplicates in the messages list.

>or should I just purge the "remove" button from the html template?

Removing the button will stop people from accidentally unlinking a
message.  But they can still unlink the message by sending the correct
URL request to the server. Depending on your threat profile, removing
the button could be fine.

You can show the "remove" button only to Admin users. This bit of TAL
(edited from one of my trackers) shows the remove form/button only if
the user has the Admin role.

 <form method="POST"  tal:condition="python:request.user.hasRole('Admin')""
     tal:attributes="action string:issue${context/id}">
   <input type="hidden" name="@remove@messages" tal:attributes="value msg/id">
   <input type="hidden" name="@action" value="edit">
   <input name="@csrf" type="hidden" tal:attributes="value
       python:utils.anti_csrf_nonce()">
   <input type="submit" name="remove" value="remove" i18n:attributes="value">
 </form>

Hope this helps.

-- rouilj