Repository: internetarchive/openlibrary

Issue:
The backend doesn't prevent logged in users from reverting anyone's changes
<!-- What problem are we solving? What does the experience look like today? What are the symptoms? -->
Any logged in user is able to utilize the revert functionality, which is supposed to be limited to admins and librarians.

### Relevant url?
<!-- `https://openlibrary.org/...` -->
Pick a work page that has more than one version.

### Steps to Reproduce
<!-- What steps caused you to find the bug? -->
1. Go to an older version of a works page in Chrome (so that the URL has "v=..." in it)
2. Open the Chrome developer tools and click on the Elements tab
3. Edit an element at the top of the body as HTML (e.g., ```<span id="top"></span>```)
4. Paste the following into the HTML (from [here](https://github.com/internetarchive/openlibrary/blob/master/openlibrary/templates/viewpage.html#L7-L11)):

```
        <div id="revertNotice">
            <form name="revert" method="POST" action="$changequery(m='revert')">
                <button type="submit">$_("Revert to this revision?")</button>
            </form>
        </div>
```
5. Replace the ```action``` value with the current URL + "&m=revert".  E.g.:
<img width="898" alt="Screen Shot 2020-11-12 at 8 26 17 PM" src="https://user-images.githubusercontent.com/72705998/99028727-64cb6100-2525-11eb-98f1-9f0343013bd8.png">

6. Click outside the text box to save it
7. Click the revert button that shows up at the top of the screen
<img width="1087" alt="Screen Shot 2020-11-12 at 8 27 37 PM" src="https://user-images.githubusercontent.com/72705998/99028866-cc81ac00-2525-11eb-8672-03a6b0a11735.png">


<!-- What actually happened after these steps? What did you expect to happen? -->
* Actual: The revert succeeds
* Expected: The revert fails

If done while logged out and you also include "&debug=true" in the ```action``` value, you get this error, which highlights the permissions check that doesn't work correctly:
<img width="853" alt="Screen Shot 2020-11-12 at 8 05 19 PM" src="https://user-images.githubusercontent.com/72705998/99028235-19fd1980-2524-11eb-905d-4e7ea81d7007.png">

### Details

- **Logged in (Y/N)?** Y
- **Browser type/version?** Chrome
- **Operating system?** 
- **Environment (prod/dev/local)?** prod
<!-- If not sure, put prod -->

### Proposal & Constraints
<!-- What is the proposed solution / implementation? Is there a precedent of this approach succeeding elsewhere? -->
Fix the permissions check.  I'm not in a position to check at the moment, but it seems like it should be something like:

```if not user or not web.ctx.site.can_write(key) or not (user.is_admin() or user.is_librarian()):```


### Related files
<!-- Files related to this issue; this is super useful for new contributors who might want to help! If you're not sure, leave this blank; a maintainer will add them. -->
The permission check is here: https://github.com/internetarchive/openlibrary/blob/master/openlibrary/plugins/upstream/code.py#L218

### Stakeholders
@mekarpeles @cdrini 


Discussion:

Resolving patch:
diff --git a/openlibrary/plugins/upstream/code.py b/openlibrary/plugins/upstream/code.py
--- a/openlibrary/plugins/upstream/code.py
+++ b/openlibrary/plugins/upstream/code.py
@@ -189,6 +189,12 @@ def setup_jquery_urls():
     web.template.Template.globals['jqueryui_url'] = jqueryui_url
     web.template.Template.globals['use_google_cdn'] = config.get('use_google_cdn', True)
 
+
+def user_is_admin_or_librarian():
+    user = web.ctx.site.get_user()
+    return user and (user.is_admin() or user.is_librarian())
+
+
 @public
 def get_document(key, limit_redirs=5):
     doc = None
@@ -214,8 +220,7 @@ def POST(self, key):
         if v is None:
             raise web.seeother(web.changequery({}))
 
-        user = web.ctx.site.get_user()
-        if not user and (user.is_admin() or user.is_librarian() and web.ctx.site.can_write(key)):
+        if not web.ctx.site.can_write(key) or not user_is_admin_or_librarian():
             return render.permission_denied(web.ctx.fullpath, "Permission denied to edit " + key + ".")
 
         thing = web.ctx.site.get(key, i.v)


Test patch: