def sidebar_menu(self): try: l = [] moderate_link = None forum_links = [] forums = DM.Forum.query.find( dict(app_config_id=c.app.config._id, parent_id=None, deleted=False)) for f in forums: if has_access(f, 'read')(): if f.url() in request.url and h.has_access(f, 'moderate')(): moderate_link = SitemapEntry( 'Moderate', "%smoderate/" % f.url(), ui_icon=g.icons['pencil'], small=DM.ForumPost.query.find({ 'discussion_id': f._id, 'status': { '$ne': 'ok' } }).count()) forum_links.append( SitemapEntry(f.name, f.url(), small=f.num_topics)) url = c.app.url + 'create_topic/' url = h.urlquote(url + c.forum.shortname if getattr(c, 'forum', None) and c.forum else url) l.append(SitemapEntry('Create Topic', url, ui_icon=g.icons['plus'])) if has_access(c.app, 'configure')(): l.append( SitemapEntry('Add Forum', c.app.url + 'new_forum', ui_icon=g.icons['conversation'])) l.append( SitemapEntry('Admin Forums', c.project.url() + 'admin/' + self.config.options.mount_point + '/forums', ui_icon=g.icons['pencil'])) if moderate_link: l.append(moderate_link) # if we are in a thread and not anonymous, provide placeholder links to use in js if '/thread/' in request.url and c.user not in ( None, M.User.anonymous()): l.append( SitemapEntry('Mark as Spam', 'flag_as_spam', ui_icon=g.icons['flag'], className='sidebar_thread_spam')) if forum_links: l.append(SitemapEntry('Forums')) l = l + forum_links l.append(SitemapEntry('Help')) l.append( SitemapEntry('Formatting Help', c.app.url + 'markdown_syntax')) return l except: # pragma no cover log.exception('sidebar_menu') return []
def test_deny_access_for_single_user(self): wiki = c.project.app_instance('wiki') user = M.User.by_username('test-user') assert has_access(wiki, 'read', user)() wiki.acl.append(M.ACE.deny(M.ProjectRole.by_user(user, upsert=True)._id, 'read', 'Spammer')) Credentials.get().clear() assert not has_access(wiki, 'read', user)()
def sidebar_menu(self): links = [] admin_url = c.project.url()+'admin/' if c.project.is_nbhd_project: links.append(SitemapEntry('Add Project', c.project.url()+'add_project', ui_icon=g.icons['plus'])) nbhd_admin_url = c.project.neighborhood.url()+'_admin/' links = links + [ SitemapEntry('Neighborhood'), SitemapEntry('Overview', nbhd_admin_url+'overview'), SitemapEntry('Awards', nbhd_admin_url+'accolades')] else: links += [SitemapEntry('Metadata', admin_url+'overview'),] if c.project.neighborhood.name != "Users": links += [ SitemapEntry('Screenshots', admin_url+'screenshots'), SitemapEntry('Categorization', admin_url+'trove') ] links.append(SitemapEntry('Tools', admin_url+'tools')) if c.project.is_root and has_access(c.project, 'admin')(): links.append(SitemapEntry('User Permissions', admin_url+'groups/')) if not c.project.is_root and has_access(c.project, 'admin')(): links.append(SitemapEntry('Permissions', admin_url+'permissions/')) if len(c.project.neighborhood_invitations): links.append(SitemapEntry('Invitation(s)', admin_url+'invitations')) links.append(SitemapEntry('Audit Trail', admin_url+ 'audit/')) if c.project.is_nbhd_project: links.append(SitemapEntry('Statistics', nbhd_admin_url+ 'stats/')) links.append(None) links.append(SitemapEntry('Help', nbhd_admin_url+ 'help/')) return links
def test_artifact(): pg = WM.Page(title='TestPage1') assert pg.project == c.project assert pg.project_id == c.project._id assert pg.app.config == c.app.config assert pg.app_config == c.app.config u = M.User.query.get(username='******') pr = u.project_role() ThreadLocalORMSession.flush_all() REGISTRY.register(allura.credentials, allura.lib.security.Credentials()) assert not security.has_access(pg, 'delete')(user=u) pg.acl.append(M.ACE.allow(pr._id, 'delete')) ThreadLocalORMSession.flush_all() c.memoize_cache = {} assert security.has_access(pg, 'delete')(user=u) pg.acl.pop() ThreadLocalORMSession.flush_all() c.memoize_cache = {} assert not security.has_access(pg, 'delete')(user=u) idx = pg.index() assert 'title' in idx assert 'url_s' in idx assert 'project_id_s' in idx assert 'mount_point_s' in idx assert 'type_s' in idx assert 'id' in idx assert idx['id'] == pg.index_id() assert 'text' in idx assert 'TestPage' in pg.shorthand_id() assert pg.link_text() == pg.shorthand_id()
def post(cls, artifact, title=None, description=None, author=None, author_link=None, author_name=None): """ Create a Feed item. Returns the item. But if anon doesn't have read access, create does not happen and None is returned """ # TODO: fix security system so we can do this correctly and fast from allura import model as M anon = M.User.anonymous() if not security.has_access(artifact, "read", user=anon): return if not security.has_access(c.project, "read", user=anon): return idx = artifact.index() if author is None: author = c.user if author_name is None: author_name = author.get_pref("display_name") if title is None: title = "%s modified by %s" % (idx["title_s"], author_name) if description is None: description = title item = cls( ref_id=artifact.index_id(), neighborhood_id=artifact.app_config.project.neighborhood_id, project_id=artifact.app_config.project_id, app_config_id=artifact.app_config_id, tool_name=artifact.app_config.tool_name, title=title, description=description, link=artifact.url(), author_name=author_name, author_link=author_link or author.url(), ) return item
def test_artifact(): pg = WM.Page(title='TestPage1') assert pg.project == c.project assert pg.project_id == c.project._id assert pg.app.config == c.app.config assert pg.app_config == c.app.config u = M.User.query.get(username='******') pr = M.ProjectRole.by_user(u, upsert=True) ThreadLocalORMSession.flush_all() REGISTRY.register(allura.credentials, allura.lib.security.Credentials()) assert not security.has_access(pg, 'delete')(user=u) pg.acl.append(M.ACE.allow(pr._id, 'delete')) ThreadLocalORMSession.flush_all() c.memoize_cache = {} assert security.has_access(pg, 'delete')(user=u) pg.acl.pop() ThreadLocalORMSession.flush_all() c.memoize_cache = {} assert not security.has_access(pg, 'delete')(user=u) idx = pg.index() assert 'title' in idx assert 'url_s' in idx assert 'project_id_s' in idx assert 'mount_point_s' in idx assert 'type_s' in idx assert 'id' in idx assert idx['id'] == pg.index_id() assert 'text' in idx assert 'TestPage' in pg.shorthand_id() assert pg.link_text() == pg.shorthand_id()
def post(cls, artifact, title=None, description=None, author=None, author_link=None, author_name=None): """ Create a Feed item. Returns the item. But if anon doesn't have read access, create does not happen and None is returned """ # TODO: fix security system so we can do this correctly and fast from allura import model as M anon = M.User.anonymous() if not security.has_access(artifact, 'read', user=anon): return if not security.has_access(c.project, 'read', user=anon): return idx = artifact.index() if author is None: author = c.user if author_name is None: author_name = author.get_pref('display_name') if title is None: title='%s modified by %s' % (idx['title_s'], author_name) if description is None: description = title item = cls( ref_id=artifact.index_id(), neighborhood_id=artifact.app_config.project.neighborhood_id, project_id=artifact.app_config.project_id, app_config_id=artifact.app_config_id, tool_name=artifact.app_config.tool_name, title=title, description=description, link=artifact.url(), author_name=author_name, author_link=author_link or author.url()) return item
def test_artifact(): pg = WM.Page(title="TestPage1") assert pg.project == c.project assert pg.project_id == c.project._id assert pg.app.config == c.app.config assert pg.app_config == c.app.config u = M.User.query.get(username="******") pr = u.project_role() ThreadLocalORMSession.flush_all() REGISTRY.register(allura.credentials, allura.lib.security.Credentials()) assert not security.has_access(pg, "delete")(user=u) pg.acl.append(M.ACE.allow(pr._id, "delete")) ThreadLocalORMSession.flush_all() c.memoize_cache = {} assert security.has_access(pg, "delete")(user=u) pg.acl.pop() ThreadLocalORMSession.flush_all() c.memoize_cache = {} assert not security.has_access(pg, "delete")(user=u) idx = pg.index() assert "title_s" in idx assert "url_s" in idx assert "project_id_s" in idx assert "mount_point_s" in idx assert "type_s" in idx assert "id" in idx assert idx["id"] == pg.index_id() assert "text" in idx assert "TestPage" in pg.shorthand_id() assert pg.link_text() == pg.shorthand_id()
def sidebar_menu(self): try: l = [] moderate_link = None forum_links = [] forums = DM.Forum.query.find(dict( app_config_id=c.app.config._id, parent_id=None, deleted=False)) for f in forums: if has_access(f,'read')(): if f.url() in request.url and h.has_access(f, 'moderate')(): moderate_link = SitemapEntry('Moderate', "%smoderate/" % f.url(), ui_icon=g.icons['pencil'], small = DM.ForumPost.query.find({'discussion_id':f._id, 'status':{'$ne': 'ok'}}).count()) forum_links.append(SitemapEntry(f.name, f.url(), small=f.num_topics)) l.append(SitemapEntry('Create Topic', c.app.url + 'create_topic', ui_icon=g.icons['plus'])) if has_access(c.app, 'configure')(): l.append(SitemapEntry('Add Forum', c.app.url + 'new_forum', ui_icon=g.icons['conversation'])) l.append(SitemapEntry('Admin Forums', c.project.url()+'admin/'+self.config.options.mount_point+'/forums', ui_icon=g.icons['pencil'])) if moderate_link: l.append(moderate_link) # if we are in a thread and not anonymous, provide placeholder links to use in js if '/thread/' in request.url and c.user not in (None, M.User.anonymous()): l.append(SitemapEntry( 'Mark as Spam', 'flag_as_spam', ui_icon=g.icons['flag'], className='sidebar_thread_spam')) if forum_links: l.append(SitemapEntry('Forums')) l = l + forum_links l.append(SitemapEntry('Help')) l.append(SitemapEntry('Formatting Help', c.app.url + 'markdown_syntax')) return l except: # pragma no cover log.exception('sidebar_menu') return []
def sidebar_menu(self): links = [] admin_url = c.project.url() + "admin/" if c.project.is_nbhd_project: links.append(SitemapEntry("Add Project", c.project.url() + "add_project", ui_icon=g.icons["plus"])) nbhd_admin_url = c.project.neighborhood.url() + "_admin/" links = links + [ SitemapEntry("Neighborhood"), SitemapEntry("Overview", nbhd_admin_url + "overview"), SitemapEntry("Awards", nbhd_admin_url + "accolades"), ] else: links += [SitemapEntry("Metadata", admin_url + "overview")] if c.project.neighborhood.name != "Users": links += [ SitemapEntry("Screenshots", admin_url + "screenshots"), SitemapEntry("Categorization", admin_url + "trove"), ] links.append(SitemapEntry("Tools", admin_url + "tools")) if c.project.is_root and has_access(c.project, "admin")(): links.append(SitemapEntry("User Permissions", admin_url + "groups/")) if not c.project.is_root and has_access(c.project, "admin")(): links.append(SitemapEntry("Permissions", admin_url + "permissions/")) if len(c.project.neighborhood_invitations): links.append(SitemapEntry("Invitation(s)", admin_url + "invitations")) links.append(SitemapEntry("Audit Trail", admin_url + "audit/")) if c.project.is_nbhd_project: links.append(SitemapEntry("Statistics", nbhd_admin_url + "stats/")) links.append(None) links.append(SitemapEntry("Help", nbhd_admin_url + "help/")) return links
def has_access(cls, artifact): # Enable only for development. # return True from allura import model as M anon = M.User.anonymous() if not security.has_access(artifact, 'read', user=anon): return False if not security.has_access(c.project, 'read', user=anon): return False return True
def test_deny_access_for_single_user(self): wiki = c.project.app_instance('wiki') user = M.User.by_username('test-user') assert has_access(wiki, 'read', user)() wiki.acl.append( M.ACE.deny( M.ProjectRole.by_user(user, upsert=True)._id, 'read', 'Spammer')) Credentials.get().clear() assert not has_access(wiki, 'read', user)()
def post(self, text, message_id=None, parent_id=None, notify=True, notification_text=None, timestamp=None, ignore_security=False, is_meta=False, subscribe=False, **kw): if not ignore_security: require_access(self, 'post') if subscribe: self.primary().subscribe() if message_id is None: message_id = h.gen_message_id() parent = parent_id and self.post_class().query.get(_id=parent_id) slug, full_slug = self.post_class().make_slugs(parent, timestamp) kwargs = dict(discussion_id=self.discussion_id, full_slug=full_slug, slug=slug, thread_id=self._id, parent_id=parent_id, text=text, status='pending', is_meta=is_meta) if timestamp is not None: kwargs['timestamp'] = timestamp if message_id is not None: kwargs['_id'] = message_id post = self.post_class()(**kwargs) if ignore_security or is_meta: spammy = False else: spammy = self.is_spam(post) # unmoderated post -> autoapprove # unmoderated post but is spammy -> don't approve it, it goes into moderation # moderated post -> moderation # moderated post but is spammy -> mark as spam if ignore_security or (not spammy and has_access(self, 'unmoderated_post')): log.info('Auto-approving message from %s', c.user.username) file_info = kw.get('file_info', None) post.approve(file_info, notify=notify, notification_text=notification_text) elif not has_access(self, 'unmoderated_post') and spammy: post.spam( submit_spam_feedback=False ) # no feedback since we're marking as spam automatically not manually else: self.notify_moderators(post) return post
def test_render_index(self): admin = M.User.query.get(username='******') anon = M.User.query.get(username="******") for app in M.AppConfig.query.find({'options.mount_point': 'bugs'}): assert has_access(app, 'create', admin) assert not has_access(app, 'create', anon) index_view = self.app.get('/bugs/') assert 'No open tickets found.' in index_view assert 'Create Ticket' in index_view # No 'Create Ticket' button for user without 'create' perm r = self.app.get('/bugs/', extra_environ=dict(username='******')) assert 'Create Ticket' not in r
def post(cls, artifact, title=None, description=None, author=None, author_link=None, author_name=None, pubdate=None, link=None, **kw): """ Create a Feed item. Returns the item. But if anon doesn't have read access, create does not happen and None is returned """ # TODO: fix security system so we can do this correctly and fast from allura import model as M anon = M.User.anonymous() if not security.has_access(artifact, 'read', user=anon): return if not security.has_access(c.project, 'read', user=anon): return idx = artifact.index() if author is None: author = c.user if author_name is None: author_name = author.get_pref('display_name') if title is None: title = '%s modified by %s' % (h.get_first(idx, 'title'), author_name) if description is None: description = title if pubdate is None: pubdate = datetime.utcnow() if link is None: link = artifact.url() item = cls(ref_id=artifact.index_id(), neighborhood_id=artifact.app_config.project.neighborhood_id, project_id=artifact.app_config.project_id, app_config_id=artifact.app_config_id, tool_name=artifact.app_config.tool_name, title=title, description=g.markdown.convert(description), link=link, pubdate=pubdate, author_name=author_name, author_link=author_link or author.url()) unique_id = kw.pop('unique_id', None) if unique_id: item.unique_id = unique_id return item
def has_activity_access(self, perm, user): """Return True if user has perm access to this object, otherwise return False. For the purposes of activitystreams, we're saying that the user does not have access to a 'comment' activity unless he also has access to the artifact on which it was posted (if there is one). """ artifact_access = True if self.thread.artifact: artifact_access = security.has_access(self.thread.artifact, perm, user, self.thread.artifact.project) return artifact_access and security.has_access(self, perm, user, self.project)
def sidebar_menu(self): links = [] admin_url = c.project.url() + 'admin/' if c.project.is_nbhd_project: links.append(SitemapEntry('Add Project', c.project.url() + 'add_project', ui_icon=g.icons['add'])) nbhd_admin_url = c.project.neighborhood.url() + '_admin/' links = links + [ SitemapEntry('Neighborhood'), SitemapEntry('Overview', nbhd_admin_url + 'overview'), SitemapEntry('Awards', nbhd_admin_url + 'accolades')] else: links += [ SitemapEntry('Welcome', admin_url), SitemapEntry('Metadata', admin_url + 'overview', className="admin-nav-metadata"), ] if c.project.neighborhood.name != "Users": links += [ SitemapEntry('Screenshots', admin_url + 'screenshots'), SitemapEntry('Categorization', admin_url + 'trove') ] if plugin.ProjectRegistrationProvider.get().registration_date(c.project) < datetime(2016, 6, 1): # only show transitional Tools page to older projects that may be used to it # no point is showing it to new projects links.append(SitemapEntry('Tools', admin_url + 'tools_moved')) if asbool(config.get('bulk_export_enabled', True)): links.append(SitemapEntry('Export', admin_url + 'export')) if c.project.is_root and has_access(c.project, 'admin')(): links.append( SitemapEntry('User Permissions', admin_url + 'groups/', className="admin-nav-user-perms")) if not c.project.is_root and has_access(c.project, 'admin')(): links.append( SitemapEntry('Permissions', admin_url + 'permissions/')) if len(c.project.neighborhood_invitations): links.append( SitemapEntry('Invitation(s)', admin_url + 'invitations')) links.append(SitemapEntry('Audit Trail', admin_url + 'audit/')) if c.project.is_nbhd_project: links.append(SitemapEntry('Statistics', nbhd_admin_url + 'stats/')) links.append(None) links.append(SitemapEntry('Help', nbhd_admin_url + 'help/')) for ep_name in sorted(g.entry_points['admin'].keys()): admin_extension = g.entry_points['admin'][ep_name] admin_extension().update_project_sidebar_menu(links) return links
def _auth_repos(self, user): def _unix_group_name(neighborhood, shortname): path = neighborhood.url_prefix + shortname[len(neighborhood.shortname_prefix) :] parts = [p for p in path.split("/") if p] if len(parts) == 2 and parts[0] == "p": parts = parts[1:] return ".".join(reversed(parts)) repos = [] for p in user.my_projects(): for p in [p] + p.direct_subprojects: for app in p.app_configs: if not issubclass(g.entry_points["tool"][app.tool_name], RepositoryApp): continue if not has_access(app, "write", user, p): continue repos.append( "/%s/%s/%s" % ( app.tool_name.lower(), _unix_group_name(p.neighborhood, p.shortname), app.options["mount_point"], ) ) repos.sort() return repos
def exfeed(self): # self.app.external_feeds_list = ['feed1', 'feed2'] # log.info("EXFEED: %s" % self.app.external_feeds_list) feeds_list = [] for feed in self.app.external_feeds_list: feeds_list.append(feed) return dict(app=self.app, feeds_list=feeds_list, allow_config=has_access(self.app, "configure")())
def has_activity_access(self, perm, user, activity): """Return True if user has perm access to this object, otherwise return False. """ if self.project is None or getattr(self, 'deleted', False): return False return security.has_access(self, perm, user, self.project)
def save(self, status=None, **kw): if status and self.req.status != status and \ (has_access(self.req, 'write') or (self.req.creator == c.user and status == 'rejected')): self.req.add_meta_post( changes={'Status': [self.req.status, status]}) self.req.status = status redirect('.')
def fork(self, project_id=None, mount_point=None, mount_label=None): # this shows the form and handles the submission security.require_authenticated() if not c.app.forkable: raise exc.HTTPNotFound from_repo = c.app.repo ThreadLocalORMSession.flush_all() ThreadLocalORMSession.close_all() from_project = c.project to_project = M.Project.query.get(_id=ObjectId(project_id)) mount_label = mount_label or '%s - %s' % ( c.project.name, c.app.config.options.mount_label) mount_point = (mount_point or from_project.shortname) if request.method != 'POST' or not mount_point: return dict(from_repo=from_repo, user_project=c.user.private_project(), mount_point=mount_point, mount_label=mount_label) else: with h.push_config(c, project=to_project): if not to_project.database_configured: to_project.configure_project(is_user_project=True) security.require(security.has_access(to_project, 'admin')) try: to_project.install_app( ep_name=from_repo.tool_name, mount_point=mount_point, mount_label=mount_label, cloned_from_project_id=from_project._id, cloned_from_repo_id=from_repo._id) redirect(to_project.url() + mount_point + '/') except exc.HTTPRedirection: raise except Exception, ex: flash(str(ex), 'error') redirect(request.referer)
def index(self, title='', text='', state='draft', labels='', limit=10, page=0, **kw): if request.method == 'POST': require_access(c.app, 'write') post = BM.BlogPost.new(title=title, state=state, text=text, labels=labels.split(','), **kw) return exc.HTTPCreated(headers=dict(Location=h.absurl('/rest' + post.url()))) else: result = RootController().index(limit=limit, page=page) posts = result['posts'] post_titles = [] for post in posts: if has_access(post, 'read')(): post_titles.append({ 'title': post.title, 'url': h.absurl('/rest' + post.url()) }) return dict(posts=post_titles, count=result['count'], limit=result['limit'], page=result['page'])
def save(self, status=None, **kw): if status and self.req.status != status and \ (has_access(self.req, 'write') or (self.req.creator == c.user and status == 'rejected')): message = self.tmpl.render(changes={'Status': [self.req.status, status]}) self.req.discussion_thread.add_post(text=message, is_meta=True) self.req.status = status redirect('.')
def search(self, q=None, history=None, project=None, limit=None, page=0, **kw): if project: redirect(c.project.url() + 'search?' + urlencode(dict(q=q, history=history))) results = [] count = 0 limit, page, start = g.handle_paging(limit, page, default=25) if not q: q = '' else: query = ['is_history_b:%s' % history, 'project_id_s:%s' % c.project._id, 'mount_point_s:%s' % c.app.config.options.mount_point, 'type_s:%s' % ShortUrl.type_s] if not has_access(c.app, 'view_private'): query.append('private_b:False') results = search(q, fq=query) if results: count = results.hits c.search_results = W.search_results return dict(q=q, history=history, results=results or [], count=count, limit=limit, page=page)
def post(self, text, message_id=None, parent_id=None, timestamp=None, ignore_security=False, is_meta=False, **kw): if not ignore_security: require_access(self, 'post') if self.ref_id and self.artifact: self.artifact.subscribe() if message_id is None: message_id = h.gen_message_id() parent = parent_id and self.post_class().query.get(_id=parent_id) slug, full_slug = self.post_class().make_slugs(parent, timestamp) kwargs = dict( discussion_id=self.discussion_id, full_slug=full_slug, slug=slug, thread_id=self._id, parent_id=parent_id, text=text, status='pending', is_meta=is_meta) if timestamp is not None: kwargs['timestamp'] = timestamp if message_id is not None: kwargs['_id'] = message_id post = self.post_class()(**kwargs) if ignore_security or not self.is_spam(post) and has_access(self, 'unmoderated_post')(): log.info('Auto-approving message from %s', c.user.username) file_info = kw.get('file_info', None) post.approve(file_info, notify=kw.get('notify', True)) else: self.notify_moderators(post) return post
def paged_query(cls, app_config, user, query, limit=None, page=0, sort=None, deleted=False, **kw): """ Query tickets, filtering for 'read' permission, sorting and paginating the result. See also paged_search which does a solr search """ limit, page, start = g.handle_paging(limit, page, default=25) q = cls.query.find(dict(query, app_config_id=app_config._id, deleted=deleted)) q = q.sort('ticket_num', pymongo.DESCENDING) if sort: field, direction = sort.split() if field.startswith('_'): field = 'custom_fields.' + field direction = dict( asc=pymongo.ASCENDING, desc=pymongo.DESCENDING)[direction] q = q.sort(field, direction) q = q.skip(start) q = q.limit(limit) tickets = [] count = q.count() for t in q: if security.has_access(t, 'read', user, app_config.project.root_project): tickets.append(t) else: count = count -1 return dict( tickets=tickets, count=count, q=json.dumps(query), limit=limit, page=page, sort=sort, **kw)
def moderate(self, **kw): require_access(self.post.thread, 'moderate') if self.thread.discussion.deleted and not has_access(c.app, 'configure')(): redirect(self.thread.discussion.url() + 'deleted') tasks.calc_thread_stats.post(self.post.thread._id) tasks.calc_forum_stats(self.post.discussion.shortname) super(ForumPostController, self).moderate(**kw)
def admin_menu(self, force_options=False): """Return the admin menu for this Application. Default implementation will return a menu with up to 4 links: - 'Permissions', if the current user has admin access to the project in which this Application is installed - 'Options', if this Application has custom options, or ``force_options`` is True - 'Rename', for editing this Application's label - 'Webhooks', if this Application can trigger any webhooks Subclasses should override this method to provide additional admin menu items. :param force_options: always include an 'Options' link in the menu, even if this Application has no custom options :return: a list of :class:`SitemapEntries <allura.app.SitemapEntry>` """ admin_url = c.project.url() + "admin/" + self.config.options.mount_point + "/" links = [] if self.permissions and has_access(c.project, "admin")(): links.append(SitemapEntry("Permissions", admin_url + "permissions")) if force_options or len(self.config_options) > 3: links.append(SitemapEntry("Options", admin_url + "options", className="admin_modal")) links.append(SitemapEntry("Rename", admin_url + "edit_label", className="admin_modal")) if len(self._webhooks) > 0: links.append(SitemapEntry("Webhooks", admin_url + "webhooks")) return links
def edit_label(self): """Renders form to update the Application's ``mount_label``. """ return dict( app=self.app, allow_config=has_access(self.app, 'configure')())
def nbhd_lookup_first_path(nbhd, name, current_user, remainder, api=False): """ Resolve first part of a neighborhood url. May raise 404, redirect, or do other side effects. Shared between NeighborhoodController and NeighborhoodRestController :param nbhd: neighborhood :param name: project or tool name (next part of url) :param current_user: a User :param remainder: remainder of url :param bool api: whether this is handling a /rest/ request or not :return: project (to be set as c.project) :return: remainder (possibly modified) """ prefix = nbhd.shortname_prefix pname = unquote(name) provider = plugin.ProjectRegistrationProvider.get() try: provider.shortname_validator.to_python(pname, check_allowed=False, neighborhood=nbhd) except Invalid: project = None else: project = M.Project.query.get(shortname=prefix + pname, neighborhood_id=nbhd._id) if project is None and prefix == 'u/': # create user-project if it is missing user = M.User.query.get(username=pname, disabled=False, pending=False) if user: project = user.private_project() if project.shortname != prefix + pname: # might be different URL than the URL requested # e.g. if username isn't valid project name and user_project_shortname() converts the name new_url = project.url() if api: new_url = '/rest' + new_url new_url += '/'.join(remainder) if request.query_string: new_url += '?' + request.query_string redirect(new_url) if project is None: # look for neighborhood tools matching the URL project = nbhd.neighborhood_project return project, (pname,) + remainder # include pname in new remainder, it is actually the nbhd tool path if project and prefix == 'u/': # make sure user-projects are associated with an enabled user user = project.user_project_of if not user or user.disabled or user.pending: raise exc.HTTPNotFound if project.database_configured is False: if remainder == ('user_icon',): redirect(g.forge_static('images/user.png')) elif current_user.username == pname: log.info('Configuring %s database for access to %r', pname, remainder) project.configure_project(is_user_project=True) else: raise exc.HTTPNotFound, pname if project is None or (project.deleted and not has_access(project, 'update')()): raise exc.HTTPNotFound, pname return project, remainder
def admin_menu(self, force_options=False): """Return the admin menu for this Application. Default implementation will return a menu with up to 3 links: - 'Permissions', if the current user has admin access to the project in which this Application is installed - 'Options', if this Application has custom options, or ``force_options`` is True - 'Label', for editing this Application's label Subclasses should override this method to provide additional admin menu items. :param force_options: always include an 'Options' link in the menu, even if this Application has no custom options :return: a list of :class:`SitemapEntries <allura.app.SitemapEntry>` """ admin_url = c.project.url() + 'admin/' + \ self.config.options.mount_point + '/' links = [] if self.permissions and has_access(c.project, 'admin')(): links.append( SitemapEntry('Permissions', admin_url + 'permissions')) if force_options or len(self.config_options) > 3: links.append( SitemapEntry('Options', admin_url + 'options', className='admin_modal')) links.append( SitemapEntry('Label', admin_url + 'edit_label', className='admin_modal')) return links
def handle_shortlink(self, lnk, sender, rcpt): art = lnk.ref.artifact if security.has_access(art, 'read', user=M.User.anonymous())(): index = art.index() text = index['snippet_s'] or h.get_first(index, 'title') url = urljoin(tg.config['base_url'], index['url_s']) self.notice(rcpt, '[%s] - [%s](%s)' % (lnk.link, text, url))
def fork(self, project_id=None, mount_point=None, mount_label=None): # this shows the form and handles the submission security.require_authenticated() if not c.app.forkable: raise exc.HTTPNotFound from_repo = c.app.repo ThreadLocalORMSession.flush_all() ThreadLocalORMSession.close_all() from_project = c.project to_project = M.Project.query.get(_id=ObjectId(project_id)) mount_label = mount_label or '%s - %s' % (c.project.name, c.app.config.options.mount_label) mount_point = (mount_point or from_project.shortname) if request.method != 'POST' or not mount_point: return dict(from_repo=from_repo, user_project=c.user.private_project(), mount_point=mount_point, mount_label=mount_label) else: with h.push_config(c, project=to_project): if not to_project.database_configured: to_project.configure_project(is_user_project=True) security.require(security.has_access(to_project, 'admin')) try: to_project.install_app( ep_name=from_repo.tool_name, mount_point=mount_point, mount_label=mount_label, cloned_from_project_id=from_project._id, cloned_from_repo_id=from_repo._id) redirect(to_project.url()+mount_point+'/') except exc.HTTPRedirection: raise except Exception, ex: flash(str(ex), 'error') redirect(request.referer)
def handle_shortlink(self, lnk, sender, rcpt): art = lnk.ref.artifact if security.has_access(art, 'read', user=M.User.anonymous())(): index = art.index() text = index['snippet_s'] or h.get_first(index, 'title') url = urljoin(tg.config.get('base_url', 'http://sourceforge.net'), index['url_s']) self.notice(rcpt, '[%s] - [%s](%s)' % (lnk.link, text,url))
def search(self, q=None, history=None, project=None, limit=None, page=0, **kw): if project: redirect(c.project.url() + 'search?' + urlencode(dict(q=q, history=history))) results = [] count = 0 limit, page, start = g.handle_paging(limit, page, default=25) if not q: q = '' else: query = [ 'is_history_b:%s' % history, 'project_id_s:%s' % c.project._id, 'mount_point_s:%s' % c.app.config.options.mount_point, 'type_s:%s' % ShortUrl.type_s ] if not has_access(c.app, 'view_private'): query.append('private_b:False') results = search(q, fq=query) if results: count = results.hits c.search_results = W.search_results return dict(q=q, history=history, results=results or [], count=count, limit=limit, page=page)
def admin_menu(self): admin_url = c.project.url() + 'admin/' + self.config.options.mount_point + '/' links = [] if has_access(self, 'configure')(): links.append(SitemapEntry('Forums', admin_url + 'forums')) links += super(ForgeDiscussionApp, self).admin_menu() return links
def post(self, text, message_id=None, parent_id=None, timestamp=None, ignore_security=False, **kw): if not ignore_security: require_access(self, 'post') if self.ref_id and self.artifact: self.artifact.subscribe() if message_id is None: message_id = h.gen_message_id() parent = parent_id and self.post_class().query.get(_id=parent_id) slug, full_slug = self.post_class().make_slugs(parent, timestamp) kwargs = dict(discussion_id=self.discussion_id, full_slug=full_slug, slug=slug, thread_id=self._id, parent_id=parent_id, text=text, status='pending') if timestamp is not None: kwargs['timestamp'] = timestamp if message_id is not None: kwargs['_id'] = message_id post = self.post_class()(**kwargs) if ignore_security or not self.is_spam(post) and has_access( self, 'unmoderated_post')(): log.info('Auto-approving message from %s', c.user.username) file_info = kw.get('file_info', None) post.approve(file_info, notify=kw.get('notify', True)) else: self.notify_moderators(post) return post
def send_direct(self, user_id): user = User.query.get(_id=ObjectId(user_id), disabled=False, pending=False) artifact = self.ref.artifact log.debug('Sending direct notification %s to user %s', self._id, user_id) # Don't send if user disabled if not user: log.debug("Skipping notification - enabled user %s not found" % user_id) return # Don't send if user doesn't have read perms to the artifact if user and artifact and \ not security.has_access(artifact, 'read', user)(): log.debug("Skipping notification - User %s doesn't have read " "access to artifact %s" % (user_id, str(self.ref_id))) log.debug("User roles [%s]; artifact ACL [%s]; PSC ACL [%s]", ', '.join([str(r) for r in security.Credentials.get().user_roles( user_id=user_id, project_id=artifact.project._id).reaching_ids]), ', '.join([str(a) for a in artifact.acl]), ', '.join([str(a) for a in artifact.parent_security_context().acl])) return allura.tasks.mail_tasks.sendmail.post( destinations=[str(user_id)], fromaddr=self.from_address, reply_to=self.reply_to_address, subject=self.subject, message_id=self._id, in_reply_to=self.in_reply_to, references=self.references, sender=self._sender(), metalink=h.absurl(self.link), text=(self.text or '') + self.footer())
def permissions(self): """Render the permissions management web page. """ from ext.admin.widgets import PermissionCard, BlockUser, BlockList c.card = PermissionCard() c.block_user = BlockUser() c.block_list = BlockList() permissions = dict((p, []) for p in self.app.permissions) block_list = defaultdict(list) for ace in self.app.config.acl: if ace.access == model.ACE.ALLOW: try: permissions[ace.permission].append(ace.role_id) except KeyError: # old, unknown permission pass elif ace.access == model.ACE.DENY: role = model.ProjectRole.query.get(_id=ace.role_id) if role.name is None and role.user: block_list[ace.permission].append((role.user, ace.reason)) return dict( app=self.app, allow_config=has_access(c.project, 'admin')(), permissions=permissions, block_list=block_list)
def options(self): """Renders form to update the Application's ``config.options``. """ return dict( app=self.app, allow_config=has_access(self.app, 'configure')())
def _auth_repos(self, user): def _unix_group_name(neighborhood, shortname): path = neighborhood.url_prefix + \ shortname[len(neighborhood.shortname_prefix):] parts = [p for p in path.split('/') if p] if len(parts) == 2 and parts[0] == 'p': parts = parts[1:] return '.'.join(reversed(parts)) repos = [] for p in user.my_projects(): for p in [p] + p.direct_subprojects: for app in p.app_configs: if not issubclass(g.entry_points["tool"][app.tool_name], RepositoryApp): continue if not has_access(app, 'write', user, p): continue repos.append( '/%s/%s/%s' % (app.tool_name.lower(), _unix_group_name(p.neighborhood, p.shortname), app.options['mount_point'])) repos.sort() return repos
def notify_moderators(self, post): ''' Notify moderators that a post needs approval [#2963] ''' artifact = self.artifact or self subject = '[%s:%s] Moderation action required' % ( c.project.shortname, c.app.config.options.mount_point) author = post.author() url = self.discussion_class().query.get(_id=self.discussion_id).url() text = ('The following submission requires approval at %s before ' 'it can be approved for posting:\n\n%s' % (h.absurl(url + 'moderate'), post.text)) n = Notification( ref_id=artifact.index_id(), topic='message', link=artifact.url(), _id=artifact.url() + post._id, from_address=str(author._id) if author != User.anonymous() else None, reply_to_address=g.noreply, subject=subject, text=text, in_reply_to=post.parent_id, author_id=author._id, pubdate=datetime.utcnow()) users = self.app_config.project.users() for u in users: if (has_access(self, 'moderate', u) and Mailbox.subscribed(user_id=u._id, app_config_id=post.app_config_id)): n.send_direct(str(u._id))
def _lookup(self, pname, *remainder): pname = unquote(pname) if not h.re_path_portion.match(pname): raise exc.HTTPNotFound, pname project = M.Project.query.get(shortname=self.prefix + pname, neighborhood_id=self.neighborhood._id) if project is None: project = self.neighborhood.neighborhood_project c.project = project return ProjectController()._lookup(pname, *remainder) if project.database_configured == False: if remainder == ('user_icon', ): redirect(g.forge_static('images/user.png')) elif c.user.username == pname: log.info('Configuring %s database for access to %r', pname, remainder) project.configure_project(is_user_project=True) else: raise exc.HTTPNotFound, pname c.project = project if project is None or (project.deleted and not has_access(c.project, 'update')()): raise exc.HTTPNotFound, pname if project.neighborhood.name != self.neighborhood_name: redirect(project.url()) return ProjectController(), remainder
def get_all(self, **kw): page_titles = [] pages = WM.Page.query.find(dict(app_config_id=c.app.config._id, deleted=False)) for page in pages: if has_access(page, 'read')(): page_titles.append(page.title) return dict(pages=page_titles)
def exfeed(self): feeds_list = [] for feed in self.app.external_feeds_list: feeds_list.append(feed) return dict(app=self.app, feeds_list=feeds_list, allow_config=has_access(self.app, 'configure')())
def permissions(self): """Render the permissions management web page. """ from ext.admin.widgets import PermissionCard, BlockUser, BlockList c.card = PermissionCard() c.block_user = BlockUser() c.block_list = BlockList() permissions = dict((p, []) for p in self.app.permissions) block_list = defaultdict(list) for ace in self.app.config.acl: if ace.access == model.ACE.ALLOW: try: permissions[ace.permission].append(ace.role_id) except KeyError: # old, unknown permission pass elif ace.access == model.ACE.DENY: role = model.ProjectRole.query.get(_id=ace.role_id) if role.name is None and role.user: block_list[ace.permission].append((role.user, ace.reason)) return dict(app=self.app, allow_config=has_access(c.project, 'admin')(), permissions=permissions, block_list=block_list)
def send_direct(self, user_id): user = User.query.get(_id=ObjectId(user_id), disabled=False) artifact = self.ref.artifact log.debug('Sending direct notification %s to user %s', self._id, user_id) # Don't send if user disabled if not user: log.debug("Skipping notification - enabled user %s not found" % user_id) return # Don't send if user doesn't have read perms to the artifact if user and artifact and \ not security.has_access(artifact, 'read', user)(): log.debug("Skipping notification - User %s doesn't have read " "access to artifact %s" % (user_id, str(self.ref_id))) log.debug("User roles [%s]; artifact ACL [%s]; PSC ACL [%s]", ', '.join([str(r) for r in security.Credentials.get().user_roles(user_id=user_id, project_id=artifact.project._id).reaching_ids]), ', '.join([str(a) for a in artifact.acl]), ', '.join([str(a) for a in artifact.parent_security_context().acl])) return allura.tasks.mail_tasks.sendmail.post( destinations=[str(user_id)], fromaddr=self.from_address, reply_to=self.reply_to_address, subject=self.subject, message_id=self._id, in_reply_to=self.in_reply_to, sender=self._sender(), text=(self.text or '') + self.footer())
def admin_menu(self, force_options=False): """Return the admin menu for this Application. Default implementation will return a menu with up to 3 links: - 'Permissions', if the current user has admin access to the project in which this Application is installed - 'Options', if this Application has custom options, or ``force_options`` is True - 'Label', for editing this Application's label Subclasses should override this method to provide additional admin menu items. :param force_options: always include an 'Options' link in the menu, even if this Application has no custom options :return: a list of :class:`SitemapEntries <allura.app.SitemapEntry>` """ admin_url = c.project.url()+'admin/'+self.config.options.mount_point+'/' links = [] if self.permissions and has_access(c.project, 'admin')(): links.append(SitemapEntry('Permissions', admin_url + 'permissions')) if force_options or len(self.config_options) > 3: links.append(SitemapEntry('Options', admin_url + 'options', className='admin_modal')) links.append(SitemapEntry('Label', admin_url + 'edit_label', className='admin_modal')) return links