mirror of https://gitlab.com/tildes/tildes.git
42 changed files with 724 additions and 94 deletions
-
63tildes/alembic/versions/50c251c4a19c_add_search_column_index_for_topics.py
-
38tildes/alembic/versions/67e332481a6e_add_two_factor_authentication.py
-
40tildes/alembic/versions/d33fb803a153_switch_to_general_permissions_column.py
-
3tildes/requirements-to-freeze.txt
-
3tildes/requirements.txt
-
1tildes/scss/_base.scss
-
2tildes/scss/modules/_btn.scss
-
8tildes/scss/modules/_sidebar.scss
-
1tildes/scss/modules/_site-header.scss
-
10tildes/scss/modules/_topic.scss
-
15tildes/sql/init/triggers/topics/topics.sql
-
11tildes/static/js/behaviors/comment-collapse-all-button.js
-
11tildes/static/js/behaviors/comment-expand-all-button.js
-
14tildes/tests/test_markdown.py
-
35tildes/tildes/__init__.py
-
7tildes/tildes/auth.py
-
15tildes/tildes/lib/markdown.py
-
3tildes/tildes/lib/ratelimit.py
-
17tildes/tildes/lib/string.py
-
5tildes/tildes/models/database_model.py
-
3tildes/tildes/models/log/log.py
-
22tildes/tildes/models/topic/topic.py
-
5tildes/tildes/models/topic/topic_query.py
-
41tildes/tildes/models/user/user.py
-
12tildes/tildes/models/user/user_invite_code.py
-
11tildes/tildes/routes.py
-
7tildes/tildes/templates/home.jinja2
-
17tildes/tildes/templates/intercooler/login_two_factor.jinja2
-
3tildes/tildes/templates/intercooler/two_factor_disabled.jinja2
-
11tildes/tildes/templates/intercooler/two_factor_enabled.jinja2
-
2tildes/tildes/templates/login.jinja2
-
31tildes/tildes/templates/search.jinja2
-
9tildes/tildes/templates/settings.jinja2
-
53tildes/tildes/templates/settings_two_factor.jinja2
-
8tildes/tildes/templates/topic.jinja2
-
25tildes/tildes/templates/topic_listing.jinja2
-
4tildes/tildes/views/api/web/comment.py
-
59tildes/tildes/views/api/web/user.py
-
6tildes/tildes/views/decorators.py
-
66tildes/tildes/views/login.py
-
34tildes/tildes/views/settings.py
-
61tildes/tildes/views/topic.py
@ -0,0 +1,63 @@ |
|||||
|
"""Add search column/index for topics |
||||
|
|
||||
|
Revision ID: 50c251c4a19c |
||||
|
Revises: d33fb803a153 |
||||
|
Create Date: 2018-08-20 19:18:04.129255 |
||||
|
|
||||
|
""" |
||||
|
from alembic import op |
||||
|
import sqlalchemy as sa |
||||
|
from sqlalchemy.dialects import postgresql |
||||
|
|
||||
|
# revision identifiers, used by Alembic. |
||||
|
revision = "50c251c4a19c" |
||||
|
down_revision = "d33fb803a153" |
||||
|
branch_labels = None |
||||
|
depends_on = None |
||||
|
|
||||
|
|
||||
|
def upgrade(): |
||||
|
op.add_column( |
||||
|
"topics", sa.Column("search_tsv", postgresql.TSVECTOR(), nullable=True) |
||||
|
) |
||||
|
op.create_index( |
||||
|
"ix_topics_search_tsv_gin", |
||||
|
"topics", |
||||
|
["search_tsv"], |
||||
|
unique=False, |
||||
|
postgresql_using="gin", |
||||
|
) |
||||
|
|
||||
|
op.execute( |
||||
|
""" |
||||
|
UPDATE topics |
||||
|
SET search_tsv = to_tsvector('pg_catalog.english', title) |
||||
|
|| to_tsvector('pg_catalog.english', COALESCE(markdown, '')); |
||||
|
""" |
||||
|
) |
||||
|
|
||||
|
op.execute( |
||||
|
""" |
||||
|
CREATE TRIGGER topic_update_search_tsv_insert |
||||
|
BEFORE INSERT ON topics |
||||
|
FOR EACH ROW |
||||
|
EXECUTE PROCEDURE tsvector_update_trigger(search_tsv, 'pg_catalog.english', title, markdown); |
||||
|
|
||||
|
CREATE TRIGGER topic_update_search_tsv_update |
||||
|
BEFORE UPDATE ON topics |
||||
|
FOR EACH ROW |
||||
|
WHEN ( |
||||
|
(OLD.title IS DISTINCT FROM NEW.title) |
||||
|
OR (OLD.markdown IS DISTINCT FROM NEW.markdown) |
||||
|
) |
||||
|
EXECUTE PROCEDURE tsvector_update_trigger(search_tsv, 'pg_catalog.english', title, markdown); |
||||
|
""" |
||||
|
) |
||||
|
|
||||
|
|
||||
|
def downgrade(): |
||||
|
op.drop_index("ix_topics_search_tsv_gin", table_name="topics") |
||||
|
op.drop_column("topics", "search_tsv") |
||||
|
|
||||
|
op.execute("DROP TRIGGER topic_update_search_tsv_insert ON topics") |
||||
|
op.execute("DROP TRIGGER topic_update_search_tsv_update ON topics") |
||||
@ -0,0 +1,38 @@ |
|||||
|
"""Add two-factor authentication |
||||
|
|
||||
|
Revision ID: 67e332481a6e |
||||
|
Revises: fab922a8bb04 |
||||
|
Create Date: 2018-07-31 02:53:50.182862 |
||||
|
|
||||
|
""" |
||||
|
from alembic import op |
||||
|
import sqlalchemy as sa |
||||
|
from sqlalchemy.dialects import postgresql |
||||
|
|
||||
|
# revision identifiers, used by Alembic. |
||||
|
revision = "67e332481a6e" |
||||
|
down_revision = "fab922a8bb04" |
||||
|
branch_labels = None |
||||
|
depends_on = None |
||||
|
|
||||
|
|
||||
|
def upgrade(): |
||||
|
op.add_column( |
||||
|
"users", |
||||
|
sa.Column( |
||||
|
"two_factor_backup_codes", postgresql.ARRAY(sa.Text()), nullable=True |
||||
|
), |
||||
|
) |
||||
|
op.add_column( |
||||
|
"users", |
||||
|
sa.Column( |
||||
|
"two_factor_enabled", sa.Boolean(), server_default="false", nullable=False |
||||
|
), |
||||
|
) |
||||
|
op.add_column("users", sa.Column("two_factor_secret", sa.Text(), nullable=True)) |
||||
|
|
||||
|
|
||||
|
def downgrade(): |
||||
|
op.drop_column("users", "two_factor_secret") |
||||
|
op.drop_column("users", "two_factor_enabled") |
||||
|
op.drop_column("users", "two_factor_backup_codes") |
||||
@ -0,0 +1,40 @@ |
|||||
|
"""Switch to general permissions column |
||||
|
|
||||
|
Revision ID: d33fb803a153 |
||||
|
Revises: 67e332481a6e |
||||
|
Create Date: 2018-08-16 23:07:07.643208 |
||||
|
|
||||
|
""" |
||||
|
from alembic import op |
||||
|
import sqlalchemy as sa |
||||
|
from sqlalchemy.dialects import postgresql |
||||
|
|
||||
|
# revision identifiers, used by Alembic. |
||||
|
revision = "d33fb803a153" |
||||
|
down_revision = "67e332481a6e" |
||||
|
branch_labels = None |
||||
|
depends_on = None |
||||
|
|
||||
|
|
||||
|
def upgrade(): |
||||
|
op.add_column( |
||||
|
"users", |
||||
|
sa.Column( |
||||
|
"permissions", postgresql.JSONB(astext_type=sa.Text()), nullable=True |
||||
|
), |
||||
|
) |
||||
|
op.drop_column("users", "is_admin") |
||||
|
|
||||
|
|
||||
|
def downgrade(): |
||||
|
op.add_column( |
||||
|
"users", |
||||
|
sa.Column( |
||||
|
"is_admin", |
||||
|
sa.BOOLEAN(), |
||||
|
server_default=sa.text("false"), |
||||
|
autoincrement=False, |
||||
|
nullable=False, |
||||
|
), |
||||
|
) |
||||
|
op.drop_column("users", "permissions") |
||||
@ -0,0 +1,11 @@ |
|||||
|
$.onmount('[data-js-comment-collapse-all-button]', function() { |
||||
|
$(this).click(function(event) { |
||||
|
$('.comment[data-comment-depth="1"]:not(.is-comment-collapsed)').each( |
||||
|
function(idx, child) { |
||||
|
$(child).find( |
||||
|
'[data-js-comment-collapse-button]:first').trigger('click'); |
||||
|
}); |
||||
|
|
||||
|
$(this).blur(); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,11 @@ |
|||||
|
$.onmount('[data-js-comment-expand-all-button]', function() { |
||||
|
$(this).click(function(event) { |
||||
|
$('.comment.is-comment-collapsed').each( |
||||
|
function(idx, child) { |
||||
|
$(child).find( |
||||
|
'[data-js-comment-collapse-button]:first').trigger('click'); |
||||
|
}); |
||||
|
|
||||
|
$(this).blur(); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,17 @@ |
|||||
|
<p>Two-factor authentication is enabled on this account. Please enter the code from your authenticator app below. If you do not have access to your authenticator device, enter a backup code.</p> |
||||
|
|
||||
|
<form class="form-narrow" method="post" action="/login_two_factor" data-ic-post-to="/login_two_factor"> |
||||
|
<input type="hidden" name="csrf_token" value="{{ get_csrf_token() }}"> |
||||
|
{% if keep %} |
||||
|
<input type="hidden" name="keep" value="on"> |
||||
|
{% endif %} |
||||
|
|
||||
|
<div class="form-group"> |
||||
|
<label class="form-label col-4" for="code">Code</label> |
||||
|
<input class="form-input" id="code" name="code" type="text" placeholder="Code" data-js-auto-focus> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-buttons"> |
||||
|
<button class="btn btn-primary" type="submit">Continue</button> |
||||
|
</div> |
||||
|
</form> |
||||
@ -0,0 +1,3 @@ |
|||||
|
<p>Two-factor authentication has been disabled. You will no longer need a code when logging in.</p> |
||||
|
|
||||
|
<p>Keep in mind: if you ever reenable two-factor authentication, your previous backup codes will not be valid.</p> |
||||
@ -0,0 +1,11 @@ |
|||||
|
<p>Congratulations! Two-factor authentication has been enabled.</p> |
||||
|
|
||||
|
<p>These are your backup codes. In the event that you lose access to your authenticator device, you will need one of these codes to regain access to your account (or disable two-factor authentication). Each code can only be used once.</p> |
||||
|
|
||||
|
<p><strong class="text-warning">Make sure to write them down and store them in a safe place.</strong></p> |
||||
|
|
||||
|
<ol> |
||||
|
{% for code in backup_codes %} |
||||
|
<li><code>{{ code }}</code></li> |
||||
|
{% endfor %} |
||||
|
</ol> |
||||
@ -0,0 +1,31 @@ |
|||||
|
{% extends 'topic_listing.jinja2' %} |
||||
|
|
||||
|
{% block title %}Search results for "{{ search }}"{% endblock %} |
||||
|
|
||||
|
{% block header_context_link %}<span class="site-header-context">Search: "{{ search }}"</span>{% endblock %} |
||||
|
|
||||
|
{% block sidebar %} |
||||
|
<form class="form-search" method="GET" action="/search"> |
||||
|
<div class="input-group"> |
||||
|
<input type="text" class="form-input input-sm" name="q" id="q" value="{{ search }}"> |
||||
|
<button class="btn btn-sm input-group-btn">Search</button> |
||||
|
</div> |
||||
|
</form> |
||||
|
|
||||
|
<h2>Search results</h2> |
||||
|
|
||||
|
<p><a href="/">Back to home page</a></p> |
||||
|
|
||||
|
{% if request.user and request.user.subscriptions %} |
||||
|
<div class="divider"></div> |
||||
|
<ul class="nav"> |
||||
|
<li>Subscriptions</li> |
||||
|
<ul class="nav"> |
||||
|
{% for subscription in request.user.subscriptions|sort(attribute='group') %} |
||||
|
<li class="nav-item"><a href="/~{{ subscription.group.path }}">~{{ subscription.group.path }}</a></li> |
||||
|
{% endfor %} |
||||
|
</ul> |
||||
|
</ul> |
||||
|
{% endif %} |
||||
|
|
||||
|
{% endblock %} |
||||
@ -0,0 +1,53 @@ |
|||||
|
{% extends 'base_no_sidebar.jinja2' %} |
||||
|
|
||||
|
{% block title %}Set up two-factor authentication{% endblock %} |
||||
|
|
||||
|
{% block main_heading %}Set up two-factor authentication{% endblock %} |
||||
|
|
||||
|
{% block content %} |
||||
|
{% if request.user.two_factor_enabled %} |
||||
|
<p>You already have two-factor authentication enabled. To disable it, enter a code from your authenticator device below and click the button. If you do not have access to your authenticator device, enter a backup code.</p> |
||||
|
|
||||
|
<form |
||||
|
name="disable-two-factor" |
||||
|
autocomplete="off" |
||||
|
data-ic-post-to="{{ request.route_url('ic_user', username=request.user.username) }}" |
||||
|
data-ic-target="closest main" |
||||
|
> |
||||
|
<div class="form-group"> |
||||
|
<label class="form-label" for="code">TOTP or backup code</label> |
||||
|
<input class="form-input" id="code" name="code" type="text" placeholder="Code" required> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-buttons"> |
||||
|
<button class="btn btn-error" type="submit">Disable two-factor authentication</button> |
||||
|
</div> |
||||
|
</form> |
||||
|
{% else %} |
||||
|
<p>To get started, you'll need to install an app such as <a href="https://support.google.com/accounts/answer/1066447">Google Authenticator</a>, <a href="https://authy.com/download">Authy</a>, <a href="https://freeotp.github.io">FreeOTP</a>, or any app that supports TOTP.</p> |
||||
|
|
||||
|
<p>Next, scan the below QR code with the app of your choice.</p> |
||||
|
|
||||
|
<img src="/settings/two_factor/qr_code" alt="" /> |
||||
|
|
||||
|
<p>Lastly, enter the 6-digit code displayed in the app.</p> |
||||
|
|
||||
|
<div class="divider"></div> |
||||
|
|
||||
|
<form |
||||
|
name="enable-two-factor" |
||||
|
autocomplete="off" |
||||
|
data-ic-post-to="{{ request.route_url('ic_user', username=request.user.username) }}" |
||||
|
data-ic-target="closest main" |
||||
|
> |
||||
|
<div class="form-group"> |
||||
|
<label class="form-label" for="code">Code</label> |
||||
|
<input class="form-input" id="code" name="code" type="text" placeholder="Code"> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-buttons"> |
||||
|
<button class="btn btn-primary" type="submit">Enable two-factor authentication</button> |
||||
|
</div> |
||||
|
</form> |
||||
|
{% endif %} |
||||
|
{% endblock %} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue