plone / plone/five.intid

Get private item info as anonymous user

Open
#13 3 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
3
Forks
0
Avg merge
2d 2h
Merged PRs (30d)
3

Description

I have a custom content type which has a RelationChoice field (z3c.relationfield.schema.RelationChoice) called relation.

In this content type view we need to proxy some info of the related item. Let's call this content type Proxy. I have an item of type Proxy, let's call it ProxyItem, and I chose a News Item for the relation field.

If ProxyItem is public and the related News Item is private, I can still access to the News Item fields as the anonymous user. I can see its title, description, etc, in the ProxyItem view.

@eikichi18 and I went through some debugging and found out that the code ProxyItem.relation.to_object returns the related News Item even if it's public.

This is what we tried in a pdb: (_object is the underlying function called by the to_object property)

> /Users/pieronicolli/.buildout/eggs/z3c.relationfield-0.7-py2.7.egg/z3c/relationfield/relation.py(112)_object()
-> try:
(Pdb) l
107     def _object(id):
108         if id is None:
109             return None
110         intids = component.getUtility(IIntIds)
111         import pdb;pdb.set_trace()
112  ->     try:
113             return intids.getObject(id)
114         except KeyError:
115             # XXX catching this error is not the right thing to do.
116             # instead, breaking a relation by removing an object should
117             # be caught and the relation should be adjusted that way.
(Pdb) n
> /Users/pieronicolli/.buildout/eggs/z3c.relationfield-0.7-py2.7.egg/z3c/relationfield/relation.py(113)_object()
-> return intids.getObject(id)
(Pdb) s
--Call--
> /Users/pieronicolli/.buildout/eggs/zope.intid-3.7.2-py2.7.egg/zope/intid/__init__.py(71)getObject()
-> def getObject(self, id):
(Pdb) l
 66             return list(self.refs.items())
 67     
 68         def __iter__(self):
 69             return self.refs.iterkeys()
 70     
 71  ->     def getObject(self, id):
 72             return self.refs[id]()
 73     
 74         def queryObject(self, id, default=None):
 75             r = self.refs.get(id)
 76             if r is not None:
(Pdb) n
> /Users/pieronicolli/.buildout/eggs/zope.intid-3.7.2-py2.7.egg/zope/intid/__init__.py(72)getObject()
-> return self.refs[id]()
(Pdb) s
--Call--
> /Users/pieronicolli/.buildout/eggs/five.intid-1.1.2-py2.7.egg/five/intid/keyreference.py(122)__call__()
-> def __call__(self):
(Pdb) l
117                     for item in reversed(chain):
118                         new_obj = aq_base(item).__of__(new_obj)
119                     obj = new_obj
120             return obj
121     
122  ->     def __call__(self):
123             return self.wrapped_object
124     
125         def __hash__(self):
126             return hash((self.dbname,
127                          self.object._p_oid,
(Pdb) n
> /Users/pieronicolli/.buildout/eggs/five.intid-1.1.2-py2.7.egg/five/intid/keyreference.py(123)__call__()
-> return self.wrapped_object
(Pdb) s
--Call--
> /Users/pieronicolli/.buildout/eggs/five.intid-1.1.2-py2.7.egg/five/intid/keyreference.py(98)wrapped_object()
-> @property
(Pdb) l
 93             # object. Asking the root object on the wrong db can trigger
 94             # an POSKeyError.
 95             connection = IConnection(self.object).get_connection(self.root_dbname)
 96             return connection[self.root_oid]
 97     
 98  ->     @property
 99         def wrapped_object(self):
100             if self.path is None:
101                 return self.object
102             try:
103                 obj = self.root.unrestrictedTraverse(self.path)
(Pdb) n
> /Users/pieronicolli/.buildout/eggs/five.intid-1.1.2-py2.7.egg/five/intid/keyreference.py(100)wrapped_object()
-> if self.path is None:
(Pdb) l
 95             connection = IConnection(self.object).get_connection(self.root_dbname)
 96             return connection[self.root_oid]
 97     
 98         @property
 99         def wrapped_object(self):
100  ->         if self.path is None:
101                 return self.object
102             try:
103                 obj = self.root.unrestrictedTraverse(self.path)
104             except (NotFound, AttributeError,):
105                 return self.object
(Pdb) self.path
'/Plone/news/my-news-item'
(Pdb) self.object
<NewsItem at my-news-item>

We stopped after noticing the unrestrictedTraverse call, and came here to ask if it's a wanted behavior or if it's not. In case it's not, should we propose a pull request changing that to a restrictedTraverse call? It feels like a possible breaking change.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with five/intid/keyreference.py, especially the wrapped_object property and its unrestrictedTraverse call, then review the relation.py _object path shown in the report. Determine whether traversal should enforce anonymous-user permissions and whether changing it to restricted traversal would be a breaking change. Done means the intended behavior is documented and private related content is not exposed through a public Proxy item.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.