def doActionFor(self, ob, action, comment=''): ''' Allows the user to request a workflow action. This method must perform its own security checks. ''' allow_review = _checkPermission('Review portal content', ob) allow_request = _checkPermission('Request review', ob) review_state = self.getReviewStateOf(ob) tool = aq_parent(aq_inner(self)) if action == 'submit': if not allow_request or review_state != 'private': raise 'Unauthorized' self.setReviewStateOf(ob, 'pending', action, comment) elif action == 'retract': if not allow_request or review_state == 'private': raise 'Unauthorized' content_creator = ob.Creator() pm = getToolByName(self, 'portal_membership') current_user = pm.getAuthenticatedMember().getUserName() if content_creator != current_user: raise 'Unauthorized' self.setReviewStateOf(ob, 'private', action, comment) elif action == 'publish': if not allow_review: raise 'Unauthorized' self.setReviewStateOf(ob, 'published', action, comment) elif action == 'reject': if not allow_review: raise 'Unauthorized' self.setReviewStateOf(ob, 'private', action, comment)
def workflowHistory(self, complete=True): """Return workflow history of this context. Taken from plone_scripts/getWorkflowHistory.py """ context = aq_inner(self.context) # check if the current user has the proper permissions if not (_checkPermission('Request review', context) or _checkPermission('Review portal content', context)): return [] workflow = getToolByName(context, 'portal_workflow') membership = getToolByName(context, 'portal_membership') review_history = [] try: # get total history review_history = workflow.getInfoFor(context, 'review_history') if not complete: # filter out automatic transitions. review_history = [r for r in review_history if r['action']] else: review_history = list(review_history) portal_type = context.portal_type anon = _(u'label_anonymous_user', default=u'Anonymous User') for r in review_history: r['type'] = 'workflow' r['transition_title'] = workflow.getTitleForTransitionOnType( r['action'], portal_type) or _("Create") r['state_title'] = workflow.getTitleForStateOnType( r['review_state'], portal_type) actorid = r['actor'] r['actorid'] = actorid if actorid is None: # action performed by an anonymous user r['actor'] = {'username': anon, 'fullname': anon} r['actor_home'] = '' else: r['actor'] = membership.getMemberInfo(actorid) if r['actor'] is not None: r['actor_home'] = self.navigation_root_url + \ '/author/' + actorid else: # member info is not available # the user was probably deleted r['actor_home'] = '' review_history.reverse() except WorkflowException: log('plone.app.layout.viewlets.content: ' '%s has no associated workflow' % context.absolute_url(), severity=logging.DEBUG) return review_history
def revisionHistory(self): context = aq_inner(self.context) if not _checkPermission(AccessPreviousVersions, context): return [] rt = getToolByName(context, "portal_repository", None) if rt is None or not rt.isVersionable(context): return [] context_url = context.absolute_url() history=rt.getHistoryMetadata(context); portal_diff = getToolByName(context, "portal_diff", None) can_diff = portal_diff is not None \ and len(portal_diff.getDiffForPortalType(context.portal_type)) > 0 can_revert = _checkPermission('CMFEditions: Revert to previous versions', context) def morphVersionDataToHistoryFormat(vdata, version_id): meta = vdata["metadata"]["sys_metadata"] userid = meta["principal"] info=dict(type='versioning', action=_(u"Edited"), transition_title=_(u"Edited"), actorid=userid, time=meta["timestamp"], comments=meta['comment'], version_id=version_id, preview_url="%s/versions_history_form?version_id=%s#version_preview" % (context_url, version_id), ) if can_diff: if version_id>0: info["diff_previous_url"]=("%s/@@history?one=%s&two=%s" % (context_url, version_id, version_id-1)) if not rt.isUpToDate(context, version_id): info["diff_current_url"]=("%s/@@history?one=current&two=%s" % (context_url, version_id)) if can_revert: info["revert_url"]="%s/revertversion" % context_url else: info["revert_url"]=None info.update(self.getUserInfo(userid)) return info # History may be an empty list if not history: return history version_history = [] retrieve = history.retrieve getId = history.getVersionId # Count backwards from most recent to least recent for i in xrange(history.getLength(countPurged=False)-1, -1, -1): version_history.append( morphVersionDataToHistoryFormat(retrieve(i, countPurged=False), getId(i, countPurged=False))) return version_history
def test__checkPermission(self): from AccessControl import getSecurityManager from AccessControl.ImplPython import ZopeSecurityPolicy from AccessControl.Permission import Permission from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManager import setSecurityPolicy from Products.CMFCore.utils import _checkPermission setSecurityPolicy(ZopeSecurityPolicy()) site = self._makeSite() newSecurityManager(None, site.acl_users.user_foo) o = site.bar_dummy Permission('View',(),o).setRoles(('Anonymous',)) Permission('WebDAV access',(),o).setRoles(('Authenticated',)) Permission('Manage users',(),o).setRoles(('Manager',)) eo = site.foo_dummy eo._owner = (['acl_users'], 'all_powerful_Oz') getSecurityManager().addContext(eo) self.failUnless( _checkPermission('View', o) ) self.failUnless( _checkPermission('WebDAV access', o) ) self.failIf( _checkPermission('Manage users', o) ) eo._proxy_roles = ('Authenticated',) self.failIf( _checkPermission('View', o) ) self.failUnless( _checkPermission('WebDAV access', o) ) self.failIf( _checkPermission('Manage users', o) ) eo._proxy_roles = ('Manager',) self.failIf( _checkPermission('View', o) ) self.failIf( _checkPermission('WebDAV access', o) ) self.failUnless( _checkPermission('Manage users', o) )
def testAnonymous(self): self.setRoles(["Manager"]) sector = self.createSector() self.logout() self.assertTrue( not _checkPermission("Euphorie: Add new RIE Content", sector)) self.assertTrue( not _checkPermission("Access contents information", sector)) self.assertTrue(not _checkPermission("Change portal events", sector)) self.assertTrue(not _checkPermission("Modify portal content", sector)) self.assertTrue(not _checkPermission("View", sector))
def testSector(self): from plone.uuid.interfaces import IUUID self.setRoles(["Manager"]) sector = self.createSector() self.login(IUUID(sector)) self.assertTrue( _checkPermission("Euphorie: Add new RIE Content", sector)) self.assertTrue( _checkPermission("Access contents information", sector)) self.assertTrue(_checkPermission("Change portal events", sector)) self.assertTrue(_checkPermission("Modify portal content", sector)) self.assertTrue(_checkPermission("View", sector))
def testManager(self): self.setRoles(["Manager"]) sector = self.createSector() self.portal.acl_users._doAddUser("manager", "secret", ["Manager"], []) self.login("manager") self.assertTrue( _checkPermission("Euphorie: Add new RIE Content", sector)) self.assertTrue( _checkPermission("Access contents information", sector)) self.assertTrue(_checkPermission("Change portal events", sector)) self.assertTrue(_checkPermission("Modify portal content", sector)) self.assertTrue(_checkPermission("View", sector))
def test_new_content_inaccessible_folder_in_path(self): # make 'layer1' not accessible by current user layer1 = self.portal.layer1 layer1.manage_permission(View, 'Manager') layer1.manage_permission(AccessContentsInformation, 'Manager') self.assertFalse(_checkPermission(AccessContentsInformation, layer1)) self.assertFalse(_checkPermission(View, layer1)) makeContent(self.folder, portal_type='RefBrowserDemo', id='accessible') obj = self.folder['accessible'] fieldname = 'singleRef' self.request.set('fieldName', fieldname) self.request.set('fieldRealName', fieldname) self.request.set('at_url', '/plone/layer1/layer2/accessible') self.assertTrue(self._getPopup(obj=obj))
def displayContentsTab(self): """See interface""" context = utils.context(self) modification_permissions = (ModifyPortalContent, AddPortalContent, DeleteObjects, ReviewPortalContent) contents_object = context # If this object is the parent folder's default page, then the # folder_contents action is for the parent, we check permissions # there. Otherwise, if the object is not folderish, we don not display # the tab. if self.isDefaultPageInFolder(): contents_object = self.getCurrentFolder() elif not self.isStructuralFolder(): return 0 # If this is not a structural folder, stop. plone_view = getMultiAdapter((contents_object, self.request), name='plone') if not plone_view.isStructuralFolder(): return 0 ## Begin monkeypatch prop_tool = getToolByName(context, 'portal_properties') use_ft = prop_tool.site_properties.getProperty('use_folder_tabs') if contents_object.portal_type not in use_ft: return 0 ## End monkeypatch show = 0 # We only want to show the 'contents' action under the following # conditions: # - If you have permission to list the contents of the relavant # object, and you can DO SOMETHING in a folder_contents view. i.e. # Copy or Move, or Modify portal content, Add portal content, # or Delete objects. # Require 'List folder contents' on the current object if _checkPermission(ListFolderContents, contents_object): # If any modifications are allowed on object show the tab. for permission in modification_permissions: if _checkPermission(permission, contents_object): show = 1 break return show
def _assertAuthorized(self, obj, permission, name=None): # We need to provide access to the repository upon the object # permissions istead of repositories method permissions. # So the repository method access is set to public and the # access is check on the object when needed. if not _checkPermission(permission, obj): raise Unauthorized(name)
def canImport(self): if self.name == "group": return parent = aq_inner(self.context) while not IAddressBook.providedBy(parent): parent = aq_parent(parent) return _checkPermission(ModifyPortalContent, parent)
def enumConfiglets(self, group=None): portal = getToolByName(self, 'portal_url').getPortalObject() context = createExprContext(self, portal, self) res = [] for a in self.listActions(): verified = 0 for permission in a.permissions: if _checkPermission(permission, portal): verified = 1 if verified and a.category == group and a.testCondition(context) \ and a.visible: res.append(a.getAction(context)) # Translate the title for sorting if getattr(self, 'REQUEST', None) is not None: for a in res: title = a['title'] if not isinstance(title, Message): title = Message(title, domain='plone') a['title'] = translate(title, context=self.REQUEST) def _id(v): return v['id'] res.sort(key=_id) return res
def changeMemberPortrait(self, portrait, id=None): """update the portait of a member. We URL-quote the member id if needed. Note that this method might be called by an anonymous user who is getting registered. This method will then be called from plone.app.users and this is fine. When called from restricted python code or with a curl command by a hacker, the declareProtected line will kick in and prevent use of this method. """ authenticated_id = self.getAuthenticatedMember().getId() if not id: id = authenticated_id safe_id = self._getSafeMemberId(id) # Our LDAP improvements hand the current user id in unicode, but BTree can't # handle unicode keys in inner objects... *sigh* if isinstance(safe_id, unicode): safe_id = str(safe_id) if authenticated_id and id != authenticated_id: # Only Managers can change portraits of others. if not _checkPermission(ManageUsers, self): raise Unauthorized # The plugable actions for how to handle the portrait. adapter = getMultiAdapter((self, self.REQUEST), IPortraitUploadAdapter) adapter(portrait, safe_id)
def show_history(self): """ History """ if not _checkPermission('CMFEditions: Access previous versions', self.context): return False return True
def changeMemberPortrait(self, portrait, id=None): """update the portait of a member. We URL-quote the member id if needed. Note that this method might be called by an anonymous user who is getting registered. This method will then be called from plone.app.users and this is fine. When called from restricted python code or with a curl command by a hacker, the declareProtected line will kick in and prevent use of this method. """ authenticated_id = self.getAuthenticatedMember().getId() if not id: id = authenticated_id safe_id = self._getSafeMemberId(id) if authenticated_id and id != authenticated_id: # Only Managers can change portraits of others. if not _checkPermission(ManageUsers, self): raise Unauthorized if portrait and portrait.filename: scaled, mimetype = scale_image(portrait) portrait = Image(id=safe_id, file=scaled, title='') membertool = getToolByName(self, 'portal_memberdata') membertool._setPortrait(portrait, safe_id)
def displayMailTab(self): # Cannot mail into the portal root if self.context.restrictedTraverse( '@@plone_context_state').is_portal_root(): return False return IFolderish.providedBy(self.context) and \ _checkPermission(AddPortalContent, self.context)
def editSyInformationProperties(self, obj , updatePeriod=None , updateFrequency=None , updateBase=None , max_items=None , REQUEST=None ): """ Edit syndication properties for the obj being passed in. These are held on the syndication_information object. Not Sitewide Properties. """ if not _checkPermission( ManageProperties, obj ): raise Unauthorized syInfo = getattr(obj, 'syndication_information', None) if syInfo is None: raise 'Syndication is Disabled' if updatePeriod: syInfo.syUpdatePeriod = updatePeriod else: syInfo.syUpdatePeriod = self.syUpdatePeriod if updateFrequency: syInfo.syUpdateFrequency = updateFrequency else: syInfo.syUpdateFrequency = self.syUpdateFrequency if updateBase: syInfo.syUpdateBase = updateBase else: syInfo.syUpdateBase = self.syUpdateBase if max_items: syInfo.max_items = max_items else: syInfo.max_items = self.max_items
def _checkAllowed(self, ec): """ Check if the action is allowed in the current context. """ container = ec.contexts['folder'] if not _checkPermission(AddPortalContent, container): return False return self.isConstructionAllowed(container)
def breadcrumbs(self): context = aq_inner(self.context) request = self.request container = utils.parent(context) try: name, item_url = get_view_url(context) except AttributeError: print context raise if container is None: return ({'absolute_url': item_url, 'Title': utils.pretty_title_or_id(context, context), },) view = getMultiAdapter((container, request), name='breadcrumbs_view') base = tuple(view.breadcrumbs()) # Some things want to be hidden from the breadcrumbs if IHideFromBreadcrumbs.providedBy(context): return base rootPath = getNavigationRoot(context) itemPath = '/'.join(context.getPhysicalPath()) # don't show default pages in breadcrumbs or pages above the navigation root if not utils.isDefaultPage(context, request) and not rootPath.startswith(itemPath): base += ({'absolute_url': _checkPermission('View', context) and item_url or None, 'Title': utils.pretty_title_or_id(context, context), },) return base
def searchResults(self, REQUEST=None, check_perms=False, **kw): mode = self.mode if mode == DISABLE_MODE: return self.patched.searchResults(REQUEST, **kw) if isinstance(REQUEST, dict): query = REQUEST.copy() else: query = {} query.update(kw) if check_perms: show_inactive = query.get('show_inactive', False) if isinstance(REQUEST, dict) and not show_inactive: show_inactive = 'show_inactive' in REQUEST user = _getAuthenticatedUser(self.catalogtool) query['allowedRolesAndUsers'] = self.catalogtool._listAllowedRolesAndUsers(user) if not show_inactive and not _checkPermission( AccessInactivePortalContent, self.catalogtool): query['effectiveRange'] = DateTime() orig_query = query.copy() # info('Running query: %s' % repr(orig_query)) try: return self.query(query) except: info("Error running Query: %s\n%s" %( repr(orig_query), traceback.format_exc())) if mode == DUAL_MODE: # fall back now... return self.patched.searchResults(REQUEST, **kw) else: return LazyMap(BrainFactory(self.catalog), [], 0)
def deleteLocalRoles(self, obj, member_ids, reindex=1, recursive=0, REQUEST=None, depth=3): """ Delete local roles of specified members. This takes far too much memory. See if we can reduce this. We have tried convincing Zope to release memory by using savepoints (transaction.savepoint(optimistic=True)). We tried using the catalog to search for only folderish items, and do explicit garbage collection. Nothing helped. Now we add a depth on which we search. This is a fluid depth. If the object does not need deletion of local roles and there are no interesting local roles at all, we decrease the depth, thus eliminating uninteresting folders where the member likely has no local roles anywhere. Yes, this may fail to delete some local roles. But at least it usually finishes within a few seconds instead of about a minute. And it does not consume over 1.5 GB of memory. So be happy. Note: with a depth of 4 you would already crawl about 90 percent of the site, so that would hardly help. """ delete = False if _checkPermission(ChangeLocalRoles, obj): has_local_roles = False for user, roles in obj.get_local_roles(): if user in member_ids: delete = True if len(roles) == 0: # I guess this cannot happen, but let's be safe. continue elif len(roles) > 1: has_local_roles = True break elif roles[0] == 'Owner': # Only one uninteresting role. continue else: has_local_roles = True break if delete: # At least one to-be-deleted role has been found. obj.manage_delLocalRoles(userids=member_ids) elif not has_local_roles: # Nothing deleted at this level, and no interesting local # roles for other users. Decrease search depth. depth -= 1 if depth <= 0: # Ignore the rest of this content tree, if any. return if recursive and hasattr(aq_base(obj), 'contentValues'): for subobj in obj.contentValues(): self.deleteLocalRoles(subobj, member_ids, 0, 1, depth=depth) if reindex and hasattr(aq_base(obj), 'reindexObjectSecurity'): # reindexObjectSecurity is always recursive obj.reindexObjectSecurity()
def check(self, sm, wf_def, ob): ''' Checks conditions in this guard. ''' pp = self.permissions if pp: found = 0 for p in pp: if _checkPermission(p, ob): found = 1 break if not found: return 0 roles = self.roles if roles: # Require at least one of the given roles. found = 0 u_roles = sm.getUser().getRolesInContext(ob) for role in roles: if role in u_roles: found = 1 break if not found: return 0 expr = self.expr if expr is not None: econtext = createExprContext(StateChangeInfo(ob, wf_def)) res = expr(econtext) if not res: return 0 return 1
def listMFUndoableTransactionsFor(self, object, first_transaction=None, last_transaction=None, PrincipiaUndoBatchSize=None, #mount_folder_path='/content' ): """ Lists all transaction IDs the user is allowed to undo inside the MountFolder (self). """ portal = self.aq_inner.aq_parent #if mount_folder_path=='/content': # mount_folder = portal.content #else: # pass # FIXME transactions = self.undoable_transactions( first_transaction=first_transaction, last_transaction=last_transaction, PrincipiaUndoBatchSize=PrincipiaUndoBatchSize) for t in transactions: # Ensure transaction ids don't have embedded LF. t['id'] = t['id'].replace('\n', '') if not _checkPermission('Manage portal', portal): # Filter out transactions done by other members of the portal. user_id = _getAuthenticatedUser(self).getId() transactions = filter( lambda record, user_id=user_id: record['user_name'].split()[-1] == user_id, transactions ) return transactions
def listGlobalActions(self, info): ''' Allows this workflow to include actions to be displayed in the actions box. Called on every request. Returns the actions to be displayed to the user. ''' if (info.isAnonymous or not _checkPermission( 'Review portal content', info.portal)): return None actions = [] catalog = getToolByName(self, 'portal_catalog', None) if catalog is not None: pending = len(catalog.searchResults( review_state='pending')) if pending > 0: actions.append( {'name': 'Pending review (%d)' % pending, 'url': info.portal_url + '/search?review_state=pending', 'permissions': (), 'category': 'global'} ) return actions
def allow_inactive(self, query_kw): """Check, if the user is allowed to see inactive content. First, check if the user is allowed to see inactive content site-wide. Second, if there is a 'path' key in the query, check if the user is allowed to see inactive content for these paths. Conservative check: as soon as one path is disallowed, return False. If a path cannot be traversed, ignore it. """ allow_inactive = _checkPermission(AccessInactivePortalContent, self) if allow_inactive: return True paths = query_kw.get('path', False) if not paths: return False if isinstance(paths, dict): # Like: {'path': {'depth': 0, 'query': ['/Plone/events/']}} # Or: {'path': {'depth': 0, 'query': '/Plone/events/'}} paths = paths.get('query', []) if isinstance(paths, six.string_types): paths = [paths] objs = [] site = getSite() for path in list(paths): if six.PY2: path = path.encode('utf-8') # paths must not be unicode try: site_path = '/'.join(site.getPhysicalPath()) parts = path[len(site_path) + 1:].split('/') parent = site.unrestrictedTraverse('/'.join(parts[:-1])) objs.append(parent.restrictedTraverse(parts[-1])) except (KeyError, AttributeError, Unauthorized): # When no object is found don't raise an error pass if not objs: return False allow = True for ob in objs: allow = allow and\ _checkPermission(AccessInactivePortalContent, ob) return allow
def _verifyActionPermissions(self, action): pp = action.get('permissions', ()) if not pp: return 1 for p in pp: if utils._checkPermission(p, self): return 1 return 0
def checkPermission(self, permissionName, object, subobjectName=None): ''' Checks whether the current user has the given permission on the given object or subobject. ''' if subobjectName is not None: object = getattr(object, subobjectName) return _checkPermission(permissionName, object)
def show_history(self): if not _checkPermission('CMFEditions: Access previous versions', self.context): return False if IViewView.providedBy(self.__parent__): return True if IFolderContentsView.providedBy(self.__parent__): return True return False
def enableSyndication(self, obj): """ Enable syndication for the obj """ if not _checkPermission(ModifyPortalContent, obj): raise Unauthorized settings = IFeedSettings(obj) settings.enabled = True
def listDAVObjects(self): # List sub-objects for PROPFIND requests. # (method is without docstring to disable publishing) # if _checkPermission(ManagePortal, self): return self.objectValues() else: return self.listFolderContents()
def mayValidate(self): """ The MeetingManager can bypass the validation process and validate an item that is in the state 'itemcreated' """ # Check if there are category and groupsInCharge, if applicable msg = self._check_required_data() if msg is not None: return msg res = False # User must have the 'Review portal content permission' if _checkPermission(ReviewPortalContent, self.context): res = True # if the current item state is 'itemcreated', only the MeetingManager can validate if self.context.queryState() in ('itemcreated',) and \ not self.context.portal_plonemeeting.isManager(self.context): res = False return res
def _setUserId(self, value): """ Set the user id. This method is defined explicitly, because: - we want to apply a different permission - we want to prevent duplicated user ids, but only when PAS _AND_ ERP5LoginUserManager are used """ existing_user_id = self.getUserId() if value != existing_user_id: if value: self.__checkUserIdAvailability( pas_plugin_class=ERP5LoginUserManager, user_id=value, ) if existing_user_id and not _checkPermission( Permissions.ManageUsers, self): raise AccessControl_Unauthorized('setUserId') self._baseSetUserId(value)
def search(self, *args, **kw): # Wrap search() the same way that searchResults() is query = {} if args: query = args[0] elif 'query_request' in kw: query = kw.get('query_request') kw['query_request'] = query.copy() user = _getAuthenticatedUser(self) query['allowedRolesAndUsers'] = self._listAllowedRolesAndUsers(user) if not _checkPermission(AccessInactivePortalContent, self): query['effectiveRange'] = DateTime() kw['query_request'] = query return super(CatalogTool, self).search(**kw)
def _checkPermissions(self, ec): """ Check permissions in the current context. """ category = self['category'] object = ec.contexts['object'] if object is not None and (category.startswith('object') or category.startswith('workflow') or category.startswith('document')): context = object else: folder = ec.contexts['folder'] if folder is not None and category.startswith('folder'): context = folder else: context = ec.contexts['portal'] for permission in self._permissions: if _checkPermission(permission, context): return True return False
def searchResults(self, REQUEST=None, check_perms=False, **kw): enabled = False if self.enabled: # need to also check is it is a search result we care about # using EL for if 'Title' in kw or 'SearchableText' in kw or 'Description' in kw: # XXX need a smarter check here... enabled = True if not enabled: if check_perms: return self.catalogtool._old_searchResults(REQUEST, **kw) else: return self.catalogtool._old_unrestrictedSearchResults(REQUEST, **kw) if isinstance(REQUEST, dict): query = REQUEST.copy() else: query = {} query.update(kw) if check_perms: show_inactive = query.get('show_inactive', False) if isinstance(REQUEST, dict) and not show_inactive: show_inactive = 'show_inactive' in REQUEST user = _getAuthenticatedUser(self.catalogtool) query['allowedRolesAndUsers'] = \ self.catalogtool._listAllowedRolesAndUsers(user) if not show_inactive and not _checkPermission( AccessInactivePortalContent, self.catalogtool): query['effectiveRange'] = DateTime() orig_query = query.copy() # info('Running query: %s' % repr(orig_query)) try: return self.search(query) except: info('Error running Query: %s\n%s' % ( repr(orig_query), traceback.format_exc())) return self.catalogtool._old_searchResults(REQUEST, **kw)
def editSyInformationProperties(self, obj, updatePeriod=None, updateFrequency=None, updateBase=None, max_items=None, REQUEST=None): """ Edit syndication properties for the obj being passed in. These are held on the syndication_information object. Not Sitewide Properties. """ if not _checkPermission(ManageProperties, obj): raise AccessControl_Unauthorized syInfo = getattr(obj, 'syndication_information', None) if syInfo is None: raise 'Syndication is Disabled' if updatePeriod is not None: syInfo.syUpdatePeriod = updatePeriod else: syInfo.syUpdatePeriod = self.syUpdatePeriod if updateFrequency is not None: syInfo.syUpdateFrequency = int(updateFrequency) else: syInfo.syUpdateFrequency = self.syUpdateFrequency if updateBase is not None: if type(updateBase) is type(''): updateBase = DateTime(updateBase) syInfo.syUpdateBase = updateBase else: syInfo.syUpdateBase = self.syUpdateBase if max_items is not None: syInfo.max_items = int(max_items) else: syInfo.max_items = self.max_items
def __call__(self, *args, **kwargs): # Security # # The minimal action consists in checking that # we have View permission on the current object # before rendering a form. Otherwise, object with # AccessContentInformation can be viewed by invoking # a form directly. # # What would be better is to prevent calling certain # forms to render objects. This can not be done # through actions since we are using sometimes forms # to render the results of a report dialog form. # An a appropriate solutions could consist in adding # a permission field to the form. Another solutions # is the use of REFERER in the rendering process. # # Both solutions are not perfect if the goal is, for # example, to prevent displaying private information of # staff. The only real solution is to use a special # permission (ex. AccessPrivateInformation) for those # properties which are sensitive. kwargs.setdefault('args', args) key_prefix = kwargs.pop('key_prefix', None) obj = getattr(self, 'aq_parent', None) if obj is not None: container = obj.aq_inner.aq_parent if not _checkPermission(Permissions.View, obj): raise AccessControl_Unauthorized('This document is not authorized for view.') else: container = None pt = getattr(self,self.pt) extra_context = dict( container=container, template=self, form=self, key_prefix=key_prefix, options=kwargs, here=obj, context=obj, ) return pt.pt_render(extra_context=extra_context)
def searchResults(self, REQUEST=None, check_perms=False, **kw): enabled = False if self.enabled: # need to also check if it is a search result we care about # using EL for if getESOnlyIndexes().intersection(kw.keys()): enabled = True if not enabled: if check_perms: return self.catalogtool._old_searchResults(REQUEST, **kw) else: return self.catalogtool._old_unrestrictedSearchResults( REQUEST, **kw) if isinstance(REQUEST, dict): query = REQUEST.copy() else: query = {} query.update(kw) if check_perms: show_inactive = query.get('show_inactive', False) if isinstance(REQUEST, dict) and not show_inactive: show_inactive = 'show_inactive' in REQUEST user = _getAuthenticatedUser(self.catalogtool) query['allowedRolesAndUsers'] = \ self.catalogtool._listAllowedRolesAndUsers(user) if not show_inactive and not _checkPermission( AccessInactivePortalContent, self.catalogtool): query['effectiveRange'] = DateTime() orig_query = query.copy() logger.debug('Running query: %s' % repr(orig_query)) try: results = self.search(query) return results except Exception: logger.error('Error running Query: {0!r}'.format(orig_query), exc_info=True) return self.catalogtool._old_searchResults(REQUEST, **kw)
def testPasswordValidity(self, password, confirm=None): """ Verify that the password satisfies the portal's requirements. o If the password is valid, return None. o If not, return a string explaining why. Comparison of password and confirm handled in Member.post_validate. """ if len(password) < 5 and not _checkPermission('Manage portal', self): return self.translate('help_password_creation', default='Passwords must contain at least ' + '5 characters.') if 'confirm_password' not in self.REQUEST.form: self.REQUEST.form['confirm_password'] = confirm errors = {} pm = getToolByName(self, 'portal_membership') user = pm.getAuthenticatedMember() if isinstance(user, SpecialUser): return None user.post_validate(self.REQUEST, errors) return errors.get('password')
def deleteLocalRoles(self, obj, member_ids, reindex=1, recursive=0, REQUEST=None): """ Delete local roles of specified members. """ if _checkPermission(ChangeLocalRoles, obj): for member_id in member_ids: if obj.get_local_roles_for_userid(userid=member_id): obj.manage_delLocalRoles(userids=member_ids) break if recursive and hasattr(aq_base(obj), 'contentValues'): for subobj in obj.contentValues(): self.deleteLocalRoles(subobj, member_ids, 0, 1) if reindex and hasattr(aq_base(obj), 'reindexObjectSecurity'): # reindexObjectSecurity is always recursive obj.reindexObjectSecurity()
def searchResults(self, REQUEST=None, **kw): """ Calls ZCatalog.searchResults with extra arguments that limit the results to what the user is allowed to see. """ user = _getAuthenticatedUser(self) kw[ 'allowedRolesAndUsers' ] = self._listAllowedRolesAndUsers( user ) if not _checkPermission( AccessInactivePortalContent, self ): base = aq_base( self ) #now = DateTime() #if hasattr( base, 'addIndex' ): # Zope 2.4 and above #kw[ 'effective' ] = { 'query' : now, 'range' : 'max' } #kw[ 'expires' ] = { 'query' : now, 'range' : 'min' } #else: # Zope 2.3 #kw[ 'effective' ] = kw[ 'expires' ] = now #kw[ 'effective_usage'] = 'range:max' #kw[ 'expires_usage' ] = 'range:min' kw['effectiveRange'] = DateTime() return apply(ZCatalog.searchResults, (self, REQUEST), kw)
def listUndoableTransactionsFor(self, object, first_transaction=None, last_transaction=None, PrincipiaUndoBatchSize=None): """ List all transaction IDs the user is allowed to undo on 'object'. """ transactions = object.undoable_transactions( first_transaction=first_transaction, last_transaction=last_transaction, PrincipiaUndoBatchSize=PrincipiaUndoBatchSize) for t in transactions: # Ensure transaction ids don't have embedded LF. t['id'] = t['id'].replace('\n', '') if not _checkPermission(ManagePortal, object): # Filter out transactions done by other members of the portal. user_id = getSecurityManager().getUser().getId() transactions = filter(lambda record, user_id=user_id: record[ 'user_name'].split()[-1] == user_id, transactions) return transactions
def overrideDiscussionFor(self, content, allowDiscussion): """ Override discussability for the given object or clear the setting. """ if not _checkPermission(ModifyPortalContent, content): raise AccessControl_Unauthorized if allowDiscussion is None or allowDiscussion == 'None': disc_flag = getattr(aq_base(content), 'allow_discussion', _marker) if disc_flag is not _marker: try: del content.allow_discussion except AttributeError: # https://bugs.launchpad.net/zope-cmf/+bug/162532 pass else: # https://bugs.launchpad.net/zope-cmf/+bug/1042836/ if allowDiscussion in ('True', 'true', 'on'): allowDiscussion = True elif allowDiscussion in ('False', 'false', 'off'): allowDiscussion = False content.allow_discussion = bool(int(allowDiscussion))
def searchResultsTrashed(self, REQUEST=None, **kw): kw = kw.copy() show_inactive = kw.get('show_inactive', False) user = _getAuthenticatedUser(self) kw['allowedRolesAndUsers'] = self._listAllowedRolesAndUsers(user) if (not show_inactive and not _checkPermission(AccessInactivePortalContent, self)): kw['effectiveRange'] = DateTime() request = getattr(self, 'REQUEST', None) if request is None: session = None else: session = getattr(self.REQUEST, 'SESSION', None) if 'trashed' not in kw: kw['trashed'] = session and session.get('trashcan', False) or False return ZCatalog.searchResults(self, REQUEST, **kw)
def getPersonalPortrait(self, id=None, verifyPermission=0): """Return a members personal portait. Modified from CMFPlone version to URL-quote the member id. """ if not id: id = self.getAuthenticatedMember().getId() safe_id = self._getSafeMemberId(id) membertool = getToolByName(self, 'portal_memberdata') portrait = membertool._getPortrait(safe_id) if isinstance(portrait, str): portrait = None if portrait is not None: if verifyPermission and not _checkPermission('View', portrait): # Don't return the portrait if the user can't get to it portrait = None if portrait is None: portal = getToolByName(self, 'portal_url').getPortalObject() portrait = getattr(portal, default_portrait, None) return portrait
def test__checkPermission(self): o = self.site.actions_dummy Permission('View', (), o).setRoles(('Anonymous', )) Permission('WebDAV access', (), o).setRoles(('Authenticated', )) Permission('Manage users', (), o).setRoles(('Manager', )) eo = self.site.content_dummy eo._owner = (['acl_users'], 'user_foo') getSecurityManager().addContext(eo) self.failUnless(_checkPermission('View', o)) self.failIf(_checkPermission('WebDAV access', o)) self.failIf(_checkPermission('Manage users', o)) eo._proxy_roles = ('Authenticated', ) self.failIf(_checkPermission('View', o)) self.failUnless(_checkPermission('WebDAV access', o)) self.failIf(_checkPermission('Manage users', o))
def editSyInformationProperties(self, obj, updatePeriod=None, updateFrequency=None, updateBase=None, max_items=None, REQUEST=None): """ Edit syndication properties for the obj being passed in. These are held on the syndication_information object. Not Sitewide Properties. """ if not _checkPermission(ManageProperties, obj): raise Unauthorized syInfo = getattr(obj, 'syndication_information', None) if syInfo is None: raise 'Syndication is Disabled' if updatePeriod: syInfo.syUpdatePeriod = updatePeriod else: syInfo.syUpdatePeriod = self.syUpdatePeriod if updateFrequency: syInfo.syUpdateFrequency = updateFrequency else: syInfo.syUpdateFrequency = self.syUpdateFrequency if updateBase: syInfo.syUpdateBase = updateBase else: syInfo.syUpdateBase = self.syUpdateBase if max_items: syInfo.max_items = max_items else: syInfo.max_items = self.max_items
def toggle(self): if not _checkPermission(WriteBudgetInfos, self.context): raise Unauthorized beforeToggleBudgetRelated = self.context.getBudgetRelated() # toggle value self.context.setBudgetRelated(not beforeToggleBudgetRelated) if beforeToggleBudgetRelated: filename = 'budgetRelatedNo.png' # prefix with 'name' so we can discriminate this label from icon name name = 'nameBudgetRelatedYes' msgid = 'budget_related_no_edit' img_title_msgid = 'budget_related_no_img_title_edit' else: filename = 'budgetRelatedYes.png' name = 'nameBudgetRelatedNo' msgid = 'budget_related_yes_edit' img_title_msgid = 'budget_related_yes_img_title_edit' label = translate(msgid, domain="PloneMeeting", context=self.request) img_title = translate(img_title_msgid, domain="PloneMeeting", context=self.request) portal_url = self.portal.absolute_url() src = "%s/%s" % (portal_url, filename) budgetRelatedClass = beforeToggleBudgetRelated and 'notBudgetRelated' or 'budgetRelated' html = self.IMG_TEMPLATE % (src, name, img_title, budgetRelatedClass, label) # reload the page if current toggle did change adviceIndex # indeed, budgetRelated informations often impact automtic advices storedAdviceIndex = self.context.adviceIndex self.context._update_after_edit(idxs=[]) if not self.context.adviceIndex == storedAdviceIndex: # we set a status reponse of 500 so the jQuery calling this # will refresh the page self.request.RESPONSE.status = 500 return return html
def setLocalRoles(self, obj, member_ids, member_role, reindex=1, REQUEST=None): """ Add local roles on an item. """ if (_checkPermission(ChangeLocalRoles, obj) and member_role in self.getCandidateLocalRoles(obj)): for member_id in member_ids: roles = list(obj.get_local_roles_for_userid(userid=member_id)) if member_role not in roles: roles.append(member_role) obj.manage_setLocalRoles(member_id, roles) if reindex: # It is assumed that all objects have the method # reindexObjectSecurity, which is in CMFCatalogAware and # thus PortalContent and PortalFolder. obj.reindexObjectSecurity()
def update_article(self): """Update an Article in Apple News""" CheckAuthenticator(self.request) if not _checkPermission('Apple News: Manage News Content', self.context): raise Unauthorized adapter = self.get_adapter() try: adapter.update_article() except AppleNewsError as e: log('Handled Apple News Error {}: {}'.format(e, e.data)) article_id = adapter.data.get('id', u'') if e.code == 409: message = _( u'unable_to_update_article_conflicts', default=u'Unable to update article (${article_id}) ' u'because it has conflicting changes. ' u'Retry again to refresh.', mapping={u'article_id': article_id} ) elif e.code == 418: message = _(u'Error article not published') else: message = _( u'error_updating_article', default=u'Error ${error_code} updating article ' u'(${article_id}). See logs for more details.', mapping={u'error_code': six.text_type(e.code) or u'', u'article_id': article_id} ) IStatusMessage(self.request).addStatusMessage(message, "error") else: article_id = adapter.data.get('id', u'') IStatusMessage(self.request).addStatusMessage( _(u'updated_article_success', default=u"Updated article with id: ${article_id}", mapping={u'article_id': article_id}), "info" )
def mayValidate(self): """ Either the Director or the MeetingManager can validate The MeetingManager can bypass the validation process and validate an item that is in the state 'itemcreated' """ res = False # Check if there are category and groupsInCharge, if applicable msg = self._check_required_data() if msg is not None: return msg user = self.context.portal_membership.getAuthenticatedMember() # first of all, the use must have the 'Review portal content permission' if _checkPermission(ReviewPortalContent, self.context) and \ (user.has_role('MeetingReviewer', self.context) or self.context.portal_plonemeeting.isManager(self.context)): res = True # if the current item state is 'itemcreated', only the MeetingManager can validate if self.context.queryState() in ('itemcreated',) and \ not self.context.portal_plonemeeting.isManager(self.context): res = False return res
def changeMemberPortrait(self, portrait, id=None): """update the portait of a member. We URL-quote the member id if needed. Note that this method might be called by an anonymous user who is getting registered. This method will then be called from plone.app.users and this is fine. When called from restricted python code or with a curl command by a hacker, the declareProtected line will kick in and prevent use of this method. """ authenticated_id = self.getAuthenticatedMember().getId() if not id: id = authenticated_id safe_id = self._getSafeMemberId(id) # dexterity.membrane hand the current user id in unicode, but BTree can't # handle unicode keys in inner objects... *sigh* if isinstance(safe_id, unicode): safe_id = str(safe_id) valid_id = id == authenticated_id or id == authenticated_id + '_large' if authenticated_id and not valid_id: # Only Managers can change portraits of others. if not _checkPermission(ManageUsers, self): raise Unauthorized if portrait and portrait.filename: if not id.endswith('_large'): # Override default resizing scaled, mimetype = convertSquareImage(portrait) else: scaled, mimetype = adjust_large_image(portrait) portrait = Image(id=safe_id, file=scaled, title='') membertool = getToolByName(self, 'portal_memberdata') membertool._setPortrait(portrait, safe_id)
def searchResults(self, REQUEST=None, **kw): # Calls ZCatalog.searchResults with extra arguments that # limit the results to what the user is allowed to see. # # This version uses the 'effectiveRange' DateRangeIndex. # # It also accepts a keyword argument show_inactive to disable # effectiveRange checking entirely even for those without portal # wide AccessInactivePortalContent permission. kw = kw.copy() show_inactive = kw.get('show_inactive', False) if isinstance(REQUEST, dict) and not show_inactive: show_inactive = 'show_inactive' in REQUEST user = _getAuthenticatedUser(self) kw['allowedRolesAndUsers'] = self._listAllowedRolesAndUsers(user) if not show_inactive \ and not _checkPermission(AccessInactivePortalContent, self): kw['effectiveRange'] = DateTime() return ZCatalog.searchResults(self, REQUEST, **kw)
def unlock(self, object, message=''): '''Unlocks an object''' if not _checkPermission(UnlockObjects, object): raise LockingError, "Inadequate permissions to unlock %s" % object locker = self.locker(object) if not locker: raise LockingError, ("Unlocking an unlocked item: %s" % pathOf(object)) user = getSecurityManager().getUser() if user.getId() != locker: raise LockingError, ("Cannot unlock %s: lock is held by %s" % (pathOf(object), locker)) # According to WriteLockInterface, we shouldn't call # wl_clearLocks(), but it seems like the right thing to do anyway. object.wl_clearLocks() if self.auto_version: vt = getToolByName(self, 'portal_versions', None) if vt is not None: vt.checkin(object, message)
def listGlobalActions(self, info): ''' Allows this workflow to include actions to be displayed in the actions box. Called on every request. Returns the actions to be displayed to the user. ''' if (info.isAnonymous or not _checkPermission('Review portal content', info.portal)): return None actions = [] catalog = getToolByName(self, 'portal_catalog', None) if catalog is not None: pending = len(catalog.searchResults(review_state='pending')) if pending > 0: actions.append({ 'name': 'Pending review (%d)' % pending, 'url': info.portal_url + '/search?review_state=pending', 'permissions': (), 'category': 'global' }) return actions
def build_security_query(self, query): # The users who has plone.AccessContent permission by prinperm # The roles who has plone.AccessContent permission by roleperm show_inactive = False # we will take care later user = _getAuthenticatedUser(self.es_catalog.catalogtool) users_roles = [ FhirString(ur) for ur in self.es_catalog.catalogtool._listAllowedRolesAndUsers(user) ] filters = list() term = T_( "allowedRolesAndUsers", value=PrimitiveTypeCollection(*users_roles), non_fhir=True, ) filters.append(term) if not show_inactive and not _checkPermission( # noqa: P001 AccessInactivePortalContent, self.es_catalog.catalogtool): value = FhirDateTime(FhirDateTime(DateTime().ISO8601())) terms = list() term = T_("effectiveRange.effectiveRange1", non_fhir=True) <= value alsoProvides(term, IIgnoreNestedCheck) terms.append(term) term = T_("effectiveRange.effectiveRange2", non_fhir=True) >= value alsoProvides(term, IIgnoreNestedCheck) terms.append(term) filters.extend(terms) # Let's finalize for f in filters: f.finalize(self) query._where.extend(filters)
def create_article(self): """Create a new Article in Apple News""" CheckAuthenticator(self.request) if not _checkPermission('Apple News: Manage News Content', self.context): raise Unauthorized adapter = self.get_adapter() try: article_data = adapter.create_article() except AppleNewsError as e: if e.code == 418: IStatusMessage(self.request).addStatusMessage( _(u"Added new article"), "info" ) return raise IStatusMessage(self.request).addStatusMessage( _(u'article_added', default=u"Added new article with id: ${article_id}", mapping={u'article_id': article_data['data']['id']}), "info" )
def __call__(self, *args, **kw): method_name = None if hasattr(self, 'req_method'): method_name = self.req_method if not method_name: return self CheckAuthenticator(self.request) if not _checkPermission('Apple News: Manage News Content', self.context): raise Unauthorized if method_name: methods = {'create-article': self.create_article, 'update-article': self.update_article, 'delete-article': self.delete_article, 'export-article': self.export_article} if method_name in methods: value = methods[method_name]() if value is None: return self.redirect() return value else: raise NotFound
def getHomeFolder(self, id=None, verifyPermission=0): """ Return a member's home folder object, or None. Specially instrumented for URL-quoted-member-id folder names. """ safe_id = self._getSafeMemberId(id) if safe_id is None: member = self.getAuthenticatedMember() if not hasattr(member, 'getMemberId'): return None safe_id = member.getMemberId() members = self.getMembersFolder() if members: try: folder = members._getOb(safe_id) if verifyPermission and not _checkPermission(View, folder): # Don't return the folder if the user can't get to it. return None return folder # KeyError added to deal with btree member folders except (AttributeError, KeyError, TypeError): pass return None
def test__checkPermission(self): from AccessControl import getSecurityManager from AccessControl.Permission import Permission from Products.CMFCore.utils import _checkPermission site = self._makeSite() o = site.actions_dummy Permission('View',(),o).setRoles(('Anonymous',)) Permission('WebDAV access',(),o).setRoles(('Authenticated',)) Permission('Manage users',(),o).setRoles(('Manager',)) eo = site.content_dummy eo._owner = (['acl_users'], 'user_foo') getSecurityManager().addContext(eo) self.failUnless( _checkPermission('View', o) ) self.failIf( _checkPermission('WebDAV access', o) ) self.failIf( _checkPermission('Manage users', o) ) eo._proxy_roles = ('Authenticated',) self.failIf( _checkPermission('View', o) ) self.failUnless( _checkPermission('WebDAV access', o) ) self.failIf( _checkPermission('Manage users', o) )