Skip to content

Commit 3a4a871

Browse files
authored
Merge pull request #50 from airbnb/nray_ensure_excluded_tags_propogate
[Feature] Make sure excluded tags are excluded everywhere
2 parents 194d245 + 5a31e81 commit 3a4a871

File tree

9 files changed

+75
-18
lines changed

9 files changed

+75
-18
lines changed

knowledge_repo/app/app.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
from .proxies import db_session, current_repo
1414
from .index import update_index
15-
from .models import db as sqlalchemy_db, Post, User
15+
from .models import db as sqlalchemy_db, Post, User, Tag
1616
from . import routes
1717

1818
logging.basicConfig(level=logging.INFO)
@@ -123,8 +123,22 @@ def update_typeahead_data(post):
123123
typeahead_data[str(post.title)] = typeahead_entry
124124

125125
update_index()
126+
127+
# For every tag in the excluded tags, create the tag object if it doesn't exist
128+
# To ensure that posts with the excluded tags do not show up in the typeahead
129+
excluded_tags = current_app.config.get('EXCLUDED_TAGS')
130+
for tag in excluded_tags:
131+
tag_exists = (db_session.query(Tag)
132+
.filter(Tag.name == tag)
133+
.all())
134+
if not tag_exists:
135+
tag_exists = Tag(name=tag)
136+
db_session.add(tag_exists)
137+
db_session.commit()
138+
126139
posts = (db_session.query(Post)
127140
.filter(Post.is_published)
141+
.filter(~Post.tags.any(Tag.name.in_(excluded_tags)))
128142
.all())
129143

130144
for post in posts:

knowledge_repo/app/models.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,10 @@ def get_liked_posts(self):
190190
.filter(Vote.user_id == self.id)
191191
.all())
192192
post_ids = [vote.object_id for vote in votes]
193+
excluded_tags = current_app.config.get('EXCLUDED_TAGS')
193194
posts = (db.session.query(Post)
194195
.filter(Post.id.in_(post_ids))
196+
.filter(~Post.tags.any(Tag.name.in_(excluded_tags)))
195197
.all())
196198
return posts
197199

@@ -307,6 +309,14 @@ def tags(self, tags):
307309

308310
self._tags = tag_objs
309311

312+
@property
313+
def contains_excluded_tag(self):
314+
excluded_tags = current_app.config.get('EXCLUDED_TAGS')
315+
for tag in self.tags:
316+
if tag.name in excluded_tags:
317+
return True
318+
return False
319+
310320
_status = db.Column('status', db.Integer(), default=0)
311321

312322
@hybrid_property

knowledge_repo/app/routes/index.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,10 @@ def render_cluster():
116116
request_tag = request.args.get('tag')
117117
sort_desc = not bool(request.args.get('sort_asc', ''))
118118

119-
post_query = db_session.query(Post).filter(Post.is_published)
119+
excluded_tags = current_app.config.get('EXCLUDED_TAGS')
120+
post_query = (db_session.query(Post)
121+
.filter(Post.is_published)
122+
.filter(~Post.tags.any(Tag.name.in_(excluded_tags))))
120123

121124
if filters:
122125
filter_set = filters.split(" ")
@@ -128,17 +131,23 @@ def render_cluster():
128131
author_to_posts = {}
129132
authors = (db_session.query(User).all())
130133
for author in authors:
131-
author_posts = [post for post in author.posts if post.is_published]
134+
author_posts = [post for
135+
post in author.posts
136+
if post.is_published and not post.contains_excluded_tag]
132137
if author_posts:
133138
author_to_posts[author.format_name] = author_posts
134139
tuples = [(k, v) for (k, v) in author_to_posts.items()]
135140

136141
elif group_by == "tags":
137142
tags_to_posts = {}
138-
all_tags = (db_session.query(Tag).all())
143+
all_tags = (db_session.query(Tag)
144+
.filter(~Tag.name.in_(excluded_tags))
145+
.all())
139146

140147
for tag in all_tags:
141-
tag_posts = [post for post in tag.posts if post.is_published]
148+
tag_posts = [post for
149+
post in tag.posts
150+
if post.is_published and not post.contains_excluded_tag]
142151
if tag_posts:
143152
tags_to_posts[tag.name] = tag.posts
144153
tuples = [(k, v) for (k, v) in tags_to_posts.items()]

knowledge_repo/app/routes/render.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ def render():
6868
if not post:
6969
raise Exception("unable to find post at {}".format(path))
7070

71+
if post.contains_excluded_tag:
72+
# It's possilbe that someone gets a direct link to a post that has an excluded tag
73+
return render_template("error.html")
74+
7175
html = render_post(post)
7276
raw_post = render_post_raw(post) if raw else None
7377

knowledge_repo/app/routes/tags.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
- /toggle_tag_subscription
1212
- /tag_list
1313
"""
14-
from flask import request, render_template, Blueprint, g
14+
from flask import current_app, request, render_template, Blueprint, g
1515
from sqlalchemy import and_
1616
import logging
1717

@@ -42,7 +42,7 @@ def render_batch_tags():
4242
sort_desc = not sort_asc
4343
feed_params = from_request_get_feed_params(request)
4444

45-
# get all tags
45+
excluded_tags = current_app.config.get('EXCLUDED_TAGS')
4646
all_tags = db_session.query(Tag).all()
4747
tags_to_posts = {}
4848
nonzero_tags = []
@@ -54,7 +54,8 @@ def render_batch_tags():
5454
db_session.delete(tag)
5555
db_session.commit()
5656

57-
tags_to_posts[tag.id] = [(post.path, post.title) for post in posts]
57+
tags_to_posts[tag.id] = [(post.path, post.title) for post in posts if
58+
post.is_published and not post.contains_excluded_tags]
5859
nonzero_tags.append(tag)
5960
# so that we can use the tag in the jinja template
6061
db_session.expunge(tag)
@@ -119,6 +120,9 @@ def render_tag_pages():
119120
if tag[0] == '#':
120121
tag = tag[1:]
121122

123+
if tag in current_app.config.get('EXCLUDED_TAGS'):
124+
return render_template('error.html')
125+
122126
tag_obj = (db_session.query(Tag)
123127
.filter(Tag.name == tag)
124128
.first())
@@ -211,6 +215,11 @@ def toggle_tag_subscription():
211215
try:
212216
# retrieve relevant tag and user args from request
213217
tag_name = request.args.get('tag_name', '')
218+
219+
if tag_name in current_app.config.get('EXCLUDED_TAGS'):
220+
logging.warning("Trying to subscribe to an excluded tag")
221+
return ""
222+
214223
subscribe_action = request.args.get('subscribe_action', '')
215224
if subscribe_action not in ['unsubscribe', 'subscribe']:
216225
logging.warning("ERROR processing request")

knowledge_repo/app/utils/emails.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@
1414

1515
logger = logging.getLogger(__name__)
1616

17-
# TODO move me to config
18-
TAGS_EXCLUDED_FROM_SUBSCRIPTION_EMAILS = ['other/private', 'private']
19-
2017

2118
def usernames_to_emails(usernames):
2219
username_to_email = current_repo.config.username_to_email
@@ -62,7 +59,7 @@ def send_subscription_emails(post):
6259
# if this post is tagged as private - send no emails
6360
post_full_tags = [tag.name for tag in post.tags]
6461
for full_tag in post_full_tags:
65-
if full_tag in TAGS_EXCLUDED_FROM_SUBSCRIPTION_EMAILS:
62+
if full_tag in current_app.config.get("EXCLUDED_TAGS"):
6663
return
6764
for tag in post.tags:
6865
send_subscription_email(post, tag)

knowledge_repo/app/utils/posts.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- get_posts
55
- get_all_post_stats
66
"""
7+
from flask import current_app
78
from sqlalchemy import func, distinct, or_
89

910
from ..app import db_session
@@ -33,12 +34,10 @@ def get_posts(feed_params):
3334
# make sure post is published
3435
query = (db_session.query(Post).filter(Post.is_published))
3536

36-
# if a private tag exists, make sure that post
37-
private_tag = (db_session.query(Tag)
38-
.filter(Tag.name == 'other/private')
39-
.first())
40-
if private_tag is not None:
41-
query = query.filter(~Post.tags.contains(private_tag))
37+
# posts returned should not include any posts in the excluded tags
38+
excluded_tags = current_app.config.get('EXCLUDED_TAGS')
39+
if excluded_tags:
40+
query = query.filter(~Post.tags.any(Tag.name.in_(excluded_tags)))
4241

4342
# filter out based on feed param filters
4443
filters = feed_params['filters']

resources/server_config.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,11 @@ def prepare_repo(repo):
8383
# WEB_EDITOR_PREFIXES to a list of supported path prefixes.
8484
# e.g. ['webposts', 'projects']
8585
WEB_EDITOR_PREFIXES = ['webposts']
86+
87+
88+
# ---------------------------------------------------
89+
# Tag configuration
90+
# ---------------------------------------------------
91+
# Posts with certain tags can be excluded from showing up
92+
# in the app. This can be useful for security purposes
93+
EXCLUDED_TAGS = ['private']

tests/config_server.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,10 @@ def prepare_repo(repo):
8888
# WEB_EDITOR_PREFIXES to a list of supported path prefixes.
8989
# e.g. ['webposts', 'projects']
9090
WEB_EDITOR_PREFIXES = None
91+
92+
# ---------------------------------------------------
93+
# Tag configuration
94+
# ---------------------------------------------------
95+
# Posts with certain tags can be excluded from showing up
96+
# in the app. This can be useful for security purposes
97+
EXCLUDED_TAGS = ['private']

0 commit comments

Comments
 (0)