From 69b5139d2ac1070791819fd6f8ac18f46fe8cdc8 Mon Sep 17 00:00:00 2001 From: Jeong Arm Date: Tue, 3 Aug 2021 18:53:02 +0900 Subject: Add Korean translations --- config/locales-glitch/ko.yml | 25 +++++++++++++++++++++++++ config/locales-glitch/simple_form.ko.yml | 20 ++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 config/locales-glitch/ko.yml create mode 100644 config/locales-glitch/simple_form.ko.yml (limited to 'config/locales-glitch') diff --git a/config/locales-glitch/ko.yml b/config/locales-glitch/ko.yml new file mode 100644 index 000000000..ae7f091bb --- /dev/null +++ b/config/locales-glitch/ko.yml @@ -0,0 +1,25 @@ +--- +ko: + admin: + dashboard: + keybase: 키베이스 연동 + settings: + enable_keybase: + desc_html: 사용자들이 키베이스를 통해 개인 신원을 증명할 수 있도록 허용 + title: 키베이스 연동 활성화 + outgoing_spoilers: + desc_html: 게시물들을 연합할 때, 열람주의가 달려있지 않다면 이 열람주의를 추가합니다. 다른 서버들이 열람주의를 하길 원하는 콘텐츠들에 특화된 서버에서 유용합니다. 미디어 또한 민감함으로 설정 됩니다. + title: 나가는 게시물에 대한 열람주의 + hide_followers_count: + desc_html: 사용자 프로필에 팔로워 수를 표시하지 않습니다 + title: 팔로워 수 숨기기 + show_reblogs_in_public_timelines: + desc_html: 공개글의 공개적인 부스트를 로컬과 공개 타임라인에 표시합니다. + title: 부스트를 공개 타임라인에 표시 + show_replies_in_public_timelines: + desc_html: 자기자신에 대한 답글(글타래)와 마찬가지로, 공개적인 답글을 로컬과 공개 타임라인에 표시합니다. + title: 답글을 공개 타임라인에 표시 + generic: + use_this: 사용하기 + settings: + flavours: 풍미 diff --git a/config/locales-glitch/simple_form.ko.yml b/config/locales-glitch/simple_form.ko.yml new file mode 100644 index 000000000..474692195 --- /dev/null +++ b/config/locales-glitch/simple_form.ko.yml @@ -0,0 +1,20 @@ +--- +en: + simple_form: + hints: + defaults: + setting_default_content_type_html: 게시물을 작성할 때, 형식을 지정하지 않았다면, 생 HTML이라고 가정합니다 + setting_default_content_type_markdown: 게시물을 작성할 때, 형식을 지정하지 않았다면, 마크다운이라고 가정합니다 + setting_default_content_type_plain: 게시물을 작성할 때, 형식을 지정하지 않았다면, 일반적인 텍스트라고 가정합니다. (마스토돈의 기본 동작) + setting_default_language: 작성하는 게시물의 언어는 자동으로 설정될 수 있습니다, 하지만 언제나 정확하지는 않습니다 + setting_skin: 선택한 마스토돈 풍미의 스킨을 바꿉니다 + labels: + defaults: + setting_default_content_type: 게시물의 기본 포맷 + setting_default_content_type_html: HTML + setting_default_content_type_markdown: 마크다운 + setting_default_content_type_plain: 일반 텍스트 + setting_favourite_modal: 관심글을 지정할 때 확인 창을 띄웁니다(글리치 풍미에만 적용됨) + setting_hide_followers_count: 내 팔로워 수 숨기기 + setting_skin: 스킨 + setting_system_emoji_font: 에모지에 시스템 기본 폰트 적용하기 (글리치 풍미에만 적용됨) -- cgit From 0843a3dd6d0855a12b22a5f6e7e24ad8b06d2f81 Mon Sep 17 00:00:00 2001 From: Jeong Arm Date: Tue, 3 Aug 2021 21:10:29 +0900 Subject: Fix typo on simple_form.ko.yml --- config/locales-glitch/simple_form.ko.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'config/locales-glitch') diff --git a/config/locales-glitch/simple_form.ko.yml b/config/locales-glitch/simple_form.ko.yml index 474692195..cd9910337 100644 --- a/config/locales-glitch/simple_form.ko.yml +++ b/config/locales-glitch/simple_form.ko.yml @@ -1,5 +1,5 @@ --- -en: +ko: simple_form: hints: defaults: -- cgit From ebb4de7d3e01f59d0eeb55d9d398875ff3266211 Mon Sep 17 00:00:00 2001 From: Julianne420 Date: Sat, 25 Sep 2021 15:30:51 +0800 Subject: Add zh-CN translations --- app/javascript/flavours/glitch/locales/zh-CN.js | 198 +++++++++++++++++++++++- config/locales-glitch/simple_form.zh-CN.yml | 20 +++ config/locales-glitch/zh-CN.yml | 25 +++ 3 files changed, 241 insertions(+), 2 deletions(-) create mode 100644 config/locales-glitch/simple_form.zh-CN.yml create mode 100644 config/locales-glitch/zh-CN.yml (limited to 'config/locales-glitch') diff --git a/app/javascript/flavours/glitch/locales/zh-CN.js b/app/javascript/flavours/glitch/locales/zh-CN.js index 944588e02..21a68fc01 100644 --- a/app/javascript/flavours/glitch/locales/zh-CN.js +++ b/app/javascript/flavours/glitch/locales/zh-CN.js @@ -1,7 +1,201 @@ import inherited from 'mastodon/locales/zh-CN.json'; const messages = { - // No translations available. + 'account.add_account_note': '为 @{name} 添加备注', + 'account.disclaimer_full': '以下信息可能无法完整代表你的个人资料。', + 'account.follows': '正在关注', + 'account.suspended_disclaimer_full': '该用户已被封禁。', + 'account.view_full_profile': '查看完整资料', + 'account_note.cancel': '取消', + 'account_note.edit': '编辑', + 'account_note.glitch_placeholder': '暂无备注', + 'account_note.save': '保存', + 'advanced_options.icon_title': '高级选项', + 'advanced_options.local-only.long': '不要发布嘟文到其他实例', + 'advanced_options.local-only.short': '本地模式', + 'advanced_options.local-only.tooltip': '这条嘟文仅限于本实例', + 'advanced_options.threaded_mode.long': '发嘟时自动打开回复', + 'advanced_options.threaded_mode.short': '线程模式', + 'advanced_options.threaded_mode.tooltip': '线程模式已启用', + 'boost_modal.missing_description': '这条嘟文未包含媒体描述', + 'column.favourited_by': '喜欢', + 'column.heading': '标题', + 'column.reblogged_by': '转嘟', + 'column.subheading': '其他选项', + 'column.toot': '嘟文和回复', + 'column_header.profile': '个人资料', + 'column_subheading.lists': '列表', + 'column_subheading.navigation': '导航', + 'community.column_settings.allow_local_only': '只显示本地模式嘟文', + 'compose.attach': '附上...', + 'compose.attach.doodle': '画点什么', + 'compose.attach.upload': '上传文件', + 'compose.content-type.html': 'HTML', + 'compose.content-type.markdown': 'Markdown', + 'compose.content-type.plain': '纯文本', + 'compose_form.poll.multiple_choices': '允许多选', + 'compose_form.poll.single_choice': '允许单选', + 'compose_form.spoiler': '隐藏为内容警告', + 'confirmation_modal.do_not_ask_again': '下次不显示确认窗口', + 'confirmations.discard_edit_media.confirm': '确认', + 'confirmations.discard_edit_media.message': '有未保存的媒体描述或预览,确认要关闭?', + 'confirmations.missing_media_description.confirm': '确认', + 'confirmations.missing_media_description.edit': '编辑', + 'confirmations.missing_media_description.message': '你没有为一种或多种媒体撰写描述。请考虑为视障人士添加描述。', + 'confirmations.unfilter': '关于此过滤后嘟文的信息', + 'confirmations.unfilter.author': '作者', + 'confirmations.unfilter.confirm': '查看', + 'confirmations.unfilter.edit_filter': '编辑过滤器', + 'confirmations.unfilter.filters': '应用 {count, plural, one {过滤器} other {过滤器}}', + 'content-type.change': '内容类型 ', + 'direct.conversations_mode': '对话模式', + 'direct.timeline_mode': '时间线模式', + 'endorsed_accounts_editor.endorsed_accounts': '推荐用户', + 'favourite_modal.combo': '下次你可以按 {combo} 跳过这个', + 'getting_started.onboarding': '参观一下', + 'getting_started.open_source_notice': 'Glitchsoc 是由 {Mastodon} 分叉出来的免费开源软件。你可以在 GitHub 上贡献或报告问题,地址是 {github}。', + 'home.column_settings.advanced': '高级', + 'home.column_settings.filter_regex': '按正则表达式过滤', + 'home.column_settings.show_direct': '显示私信', + 'home.settings': '列表设置', + 'keyboard_shortcuts.bookmark': '书签', + 'keyboard_shortcuts.secondary_toot': '使用二级隐私设置发送嘟文', + 'keyboard_shortcuts.toggle_collapse': '折叠或展开嘟文', + 'layout.auto': '自动模式', + 'layout.current_is': '你目前的布局是:', + 'layout.desktop': '桌面模式', + 'layout.hint.auto': '根据“启用高级 Web 界面”设置和屏幕大小自动选择布局。', + 'layout.hint.desktop': '“使用多列布局,无论“启用高级 Web 界面”设置和屏幕大小如何。', + 'layout.hint.single': '使用单列布局,无论“启用高级 Web 界面”设置和屏幕大小如何。', + 'layout.single': '移动模式', + 'media_gallery.sensitive': '敏感内容', + 'moved_to_warning': '此帐户已被标记为移至 {moved_to_link},并且似乎没有收到新关注者。', + 'navigation_bar.app_settings': '应用选项', + 'navigation_bar.featured_users': '推荐用户', + 'navigation_bar.misc': '杂项', + 'notification.markForDeletion': '标记以删除', + 'notification_purge.btn_all': '全选', + 'notification_purge.btn_apply': '清除已选', + 'notification_purge.btn_invert': '反向选择', + 'notification_purge.btn_none': '取消全选', + 'notification_purge.start': '进入通知清除模式', + 'notifications.clear': '清除所有通知', + 'notifications.marked_clear': '清除选择的通知', + 'notifications.marked_clear_confirmation': '你确定要永久清除所有选择的通知吗?', + 'onboarding.done': '完成', + 'onboarding.next': '下一个', + 'onboarding.page_five.public_timelines': '本地时间线显示来自 {domain} 中所有人的公开嘟文。跨站时间线显示了 {domain} 用户关注的每个人的公开嘟文。这些被称为公共时间线,是发现新朋友的好方法。', + 'onboarding.page_four.home': '你的主页时间线会显示你关注的人的嘟文。', + 'onboarding.page_four.notifications': '通知栏显示某人与你互动的内容。', + 'onboarding.page_one.federation': '{domain} 是 Mastodon 的一个“实例”。Mastodon 是一个由独立服务器组成的,通过不断联合形成的社交网络。我们称这些服务器为实例。', + 'onboarding.page_one.handle': '你位于 {domain},因此你的完整用户名是 {handle} 。', + 'onboarding.page_one.welcome': '欢迎来到 {domain}!', + 'onboarding.page_six.admin': '实例的管理员是 {admin}。', + 'onboarding.page_six.almost_done': '就快完成了...', + 'onboarding.page_six.appetoot': '尽情享用吧!', + 'onboarding.page_six.apps_available': '有适用于 iOS、Android 和其他平台的应用程序。', + 'onboarding.page_six.github': '{domain} 在 Glitchsoc 上运行。Glitchsoc 是 {Mastodon} 的一个友好 {fork},与任何 Mastodon 实例或应用兼容。Glitchsoc 是完全免费和开源的。你可以在 {github} 上报告错误、请求功能或贡献代码。', + 'onboarding.page_six.guidelines': '社区准则', + 'onboarding.page_six.read_guidelines': '请阅读 {domain} 的 {guidelines}!', + 'onboarding.page_six.various_app': '应用程序', + 'onboarding.page_three.profile': '编辑你的个人资料,更改你的头像、个人简介和昵称。在那里,你还会发现其他设置。', + 'onboarding.page_three.search': '使用搜索栏查找用户并查看标签,例如 #illustration 和 #introductions。要查找不在此实例中的用户,请使用他们的完整用户名。', + 'onboarding.page_two.compose': '在撰写框中撰写嘟文。你可以使用下方图标上传图像、更改隐私设置和添加内容警告。', + 'onboarding.skip': '跳过', + 'settings.always_show_spoilers_field': '始终显示内容警告框', + 'settings.auto_collapse': '自动折叠', + 'settings.auto_collapse_all': '所有', + 'settings.auto_collapse_lengthy': '长嘟文', + 'settings.auto_collapse_media': '带媒体文件的嘟文', + 'settings.auto_collapse_notifications': '通知', + 'settings.auto_collapse_reblogs': '转嘟', + 'settings.auto_collapse_replies': '回复', + 'settings.close': '关闭', + 'settings.collapsed_statuses': '折叠嘟文', + 'settings.compose_box_opts': '撰写框', + 'settings.confirm_before_clearing_draft': '在覆盖正在写入的嘟文之前显示确认对话框', + 'settings.confirm_boost_missing_media_description': '在转嘟缺少媒体描述的嘟文之前显示确认对话框', + 'settings.confirm_missing_media_description': '在发送缺少媒体描述的嘟文之前显示确认对话框', + 'settings.content_warnings': '内容警告', + 'settings.content_warnings.regexp': '正则表达式', + 'settings.content_warnings_filter': '不会自动展开的内容警告:', + 'settings.enable_collapsed': '启用折叠嘟文', + 'settings.enable_content_warnings_auto_unfold': '自动展开内容警告', + 'settings.filtering_behavior': '过滤器行为', + 'settings.filtering_behavior.cw': '仍然显示嘟文,并在内容警告中添加过滤词', + 'settings.filtering_behavior.drop': '完全隐藏过滤的嘟文', + 'settings.filtering_behavior.hide': '显示“已过滤”并添加一个按钮来显示原因', + 'settings.filtering_behavior.upstream': '像原版 Mastodon 一样显示“已过滤”', + 'settings.filters': '过滤器', + 'settings.general': '一般', + 'settings.hicolor_privacy_icons': '彩色隐私图标 ', + 'settings.hicolor_privacy_icons.hint': '以明亮且易于区分的颜色显示隐私图标', + 'settings.image_backgrounds': '图片背景', + 'settings.image_backgrounds_media': '预览折叠嘟文的媒体文件', + 'settings.image_backgrounds_users': '为折叠嘟文附加图片背景', + 'settings.inline_preview_cards': '外部链接的内嵌预览卡片', + 'settings.layout': '布局:', + 'settings.layout_opts': '布局选项', + 'settings.media': '媒体', + 'settings.media_fullwidth': '全宽媒体预览', + 'settings.media_letterbox': '信箱媒体', + 'settings.media_letterbox_hint': '缩小媒体以填充图像容器而不是拉伸和裁剪它们', + 'settings.media_reveal_behind_cw': '默认显示内容警告后的敏感媒体', + 'settings.navbar_under': '底部导航栏(仅限于移动模式)', + 'settings.notifications.favicon_badge': '未读通知网站图标', + 'settings.notifications.favicon_badge.hint': '将未读通知添加到网站图标', + 'settings.notifications.tab_badge': '未读通知图标', + 'settings.notifications.tab_badge.hint': '当通知栏未打开时,显示未读通知图标', + 'settings.notifications_opts': '通知选项', + 'settings.pop_in_left': '左边', + 'settings.pop_in_player': '启用悬浮播放器', + 'settings.pop_in_position': '悬浮播放器位置:', + 'settings.pop_in_right': '右边', + 'settings.preferences': '用户选项', + 'settings.prepend_cw_re': '回复时在内容警告前加上“re:”', + 'settings.preselect_on_reply': '回复时预先选择用户名', + 'settings.preselect_on_reply_hint': '回复与多个参与者的对话时,预先选择第一个用户名', + 'settings.rewrite_mentions': '重写嘟文中的提及', + 'settings.rewrite_mentions_acct': '重写为用户名和域名(当帐户为远程时)', + 'settings.rewrite_mentions_no': '不要重写', + 'settings.rewrite_mentions_username': '重写为用户名', + 'settings.show_action_bar': '在折叠的嘟文中显示操作按钮', + 'settings.show_content_type_choice': '允许你在撰写嘟文时选择格式类型', + 'settings.show_reply_counter': '显示回复的大致数量', + 'settings.side_arm': '辅助发嘟按钮:', + 'settings.side_arm.none': '无', + 'settings.side_arm_reply_mode': '当回复嘟文时:', + 'settings.side_arm_reply_mode.copy': '复制被回复嘟文的隐私设置', + 'settings.side_arm_reply_mode.keep': '保留辅助发嘟按钮以设置隐私', + 'settings.side_arm_reply_mode.restrict': '将隐私设置限制为正在回复的那条嘟文', + 'settings.swipe_to_change_columns': '允许滑动以在列之间切换(仅限移动模式)', + 'settings.tag_misleading_links': '标记误导性链接', + 'settings.tag_misleading_links.hint': '将带有目标网页链接的视觉指示添加到每个未明确的链接', + 'settings.wide_view': '宽视图(仅限于桌面模式)', + 'settings.wide_view_hint': '拉伸列宽以更好地填充可用空间。', + 'status.collapse': '折叠', + 'status.has_audio': '附带音频文件', + 'status.has_pictures': '附带图片文件', + 'status.has_preview_card': '附带预览卡片', + 'status.has_video': '附带视频文件', + 'status.hide': '隐藏内容', + 'status.in_reply_to': '此嘟文是回复', + 'status.is_poll': '此嘟文是投票', + 'status.local_only': '此嘟文仅本实例可见', + 'status.sensitive_toggle': '点击查看', + 'status.show_filter_reason': '(显示原因)', + 'status.uncollapse': '不折叠', + 'upload_modal.applying': '正在应用...', + 'web_app_crash.change_your_settings': '更改 {settings}', + 'web_app_crash.content': '你可以尝试这些:', + 'web_app_crash.debug_info': '调试信息', + 'web_app_crash.disable_addons': '禁用浏览器插件或本地翻译工具', + 'web_app_crash.issue_tracker': '问题追踪器', + 'web_app_crash.reload': '刷新', + 'web_app_crash.reload_page': '{reload} 此页面', + 'web_app_crash.report_issue': '将错误报告给 {issuetracker}', + 'web_app_crash.settings': '设置', + 'web_app_crash.title': '抱歉,Mastodon 出了点问题。', }; -export default Object.assign({}, inherited, messages); +export default Object.assign({}, inherited, messages); \ No newline at end of file diff --git a/config/locales-glitch/simple_form.zh-CN.yml b/config/locales-glitch/simple_form.zh-CN.yml new file mode 100644 index 000000000..a82617fdb --- /dev/null +++ b/config/locales-glitch/simple_form.zh-CN.yml @@ -0,0 +1,20 @@ +--- +zh-CN: + simple_form: + hints: + defaults: + setting_default_content_type_html: 在撰写嘟文时,除非另有指定,假定它们使用原始 HTML 语言撰写 + setting_default_content_type_markdown: 在撰写嘟文时,除非另有指定,假定它们使用 Markdown 进行富文本格式化 + setting_default_content_type_plain: 在撰写嘟文时,除非另有指定,假定它们是没有特殊格式的纯文本(默认的 Mastodon 行为) + setting_default_language: 你的嘟文语言可以自动检测,但不一定准确 + setting_skin: 更换为所选择的 Mastodon 风味 + labels: + defaults: + setting_default_content_type: 嘟文的默认格式 + setting_default_content_type_html: HTML + setting_default_content_type_markdown: Markdown + setting_default_content_type_plain: 纯文本 + setting_favourite_modal: 在喜欢嘟文前询问我 (仅限于 Glitch 风味) + setting_hide_followers_count: 隐藏你的关注者人数 + setting_skin: 皮肤 + setting_system_emoji_font: 表情符号使用系统默认字体 (仅限于 Glitch 风味) \ No newline at end of file diff --git a/config/locales-glitch/zh-CN.yml b/config/locales-glitch/zh-CN.yml new file mode 100644 index 000000000..ea1db3eb9 --- /dev/null +++ b/config/locales-glitch/zh-CN.yml @@ -0,0 +1,25 @@ +--- +zh-CN: + admin: + dashboard: + keybase: Keybase 集成 + settings: + enable_keybase: + desc_html: 允许你的用户使用 Keybase 证明身份 + title: 启用 Keybase 集成 + outgoing_spoilers: + desc_html: 在联邦化嘟文的时候,将这个内容警告添加到没有内容警告的嘟文中。如果你的服务器专用于其他服务器可能希望有内容警告的内容,它会很有用。媒体也将被标记为敏感。 + title: 对外嘟文的内容警告 + hide_followers_count: + desc_html: 不要在用户资料中显示关注者人数 + title: 隐藏关注者人数 + show_reblogs_in_public_timelines: + desc_html: 在本地和跨站时间线中显示公开嘟文的公开转嘟。 + title: 在公共时间线中显示转嘟 + show_replies_in_public_timelines: + desc_html: 除了公开的自我回复(线程模式),在本地和跨站时间轴中显示公开回复。 + title: 在公共时间轴中显示回复 + generic: + use_this: 使用这个 + settings: + flavours: 风味 -- cgit From 7bc7da565bc3fa512a5b1c23bea71efc6279a899 Mon Sep 17 00:00:00 2001 From: atsu1125 Date: Wed, 20 Oct 2021 13:04:50 +0900 Subject: translation glitch's preferences [ja] --- config/locales-glitch/ja.yml | 19 +++++++++++++++++++ config/locales-glitch/simple_form.ja.yml | 14 ++++++++++++++ 2 files changed, 33 insertions(+) (limited to 'config/locales-glitch') diff --git a/config/locales-glitch/ja.yml b/config/locales-glitch/ja.yml index 15fb6e566..2be4e9ea0 100644 --- a/config/locales-glitch/ja.yml +++ b/config/locales-glitch/ja.yml @@ -1,5 +1,24 @@ --- ja: + admin: + dashboard: + keybase: Keybase統合 + settings: + enable_keybase: + desc_html: Keybaseにより身元の証明が可能となります + title: Keybase統合を有効にする + outgoing_spoilers: + desc_html: トゥートが連合される際、閲覧注意としてマークされていないトゥートにこの警告が追加されます。これはあなたのインスタンスが他のインスタンスに警告をして欲しいとされる投稿に特化している場合に便利です。 メディアは閲覧注意にマークされます。 + title: 発信するトゥートへの警告 + hide_followers_count: + desc_html: プロフィールページのフォロワー数を見られないようにします + title: フォロワー数を隠す + show_reblogs_in_public_timelines: + desc_html: ローカルタイムラインと連合タイムラインに公開投稿のブーストを表示します + title: 公開タイムラインにブーストを表示 + show_replies_in_public_timelines: + desc_html: 自分への公開投稿の返信に加えて、すべての公開投稿の返信をローカルタイムラインと連合タイムラインに表示します。 + title: 公開タイムラインに返信を表示 generic: use_this: これを使う settings: diff --git a/config/locales-glitch/simple_form.ja.yml b/config/locales-glitch/simple_form.ja.yml index ba02c8091..dc9e0b579 100644 --- a/config/locales-glitch/simple_form.ja.yml +++ b/config/locales-glitch/simple_form.ja.yml @@ -1,6 +1,20 @@ --- ja: simple_form: + hints: + defaults: + setting_default_content_type_html: トゥートを作成するとき特に指定がない限り生のHTMLで書かれているとします + setting_default_content_type_markdown: トゥートを作成するとき特に指定がない限りリッチテキスト形式のマークダウンで書かれているとします + setting_default_content_type_plain: トゥートを作成するとき特に指定がない限りプレーンテキストで書かれているとします(Mastodon既定の動作) + setting_default_language: あなたのトゥートの言語を自動検出しますが必ずしも正確ではありません + setting_skin: 選択したMastodonフレーバーに変更します labels: defaults: + setting_default_content_type: 既定のトゥート形式 + setting_default_content_type_html: HTML + setting_default_content_type_markdown: マークダウン + setting_default_content_type_plain: プレーンテキスト setting_favourite_modal: お気に入りをする前に確認ダイアログを表示する + setting_hide_followers_count: フォロワー数を隠す + setting_skin: スキン + setting_system_emoji_font: 絵文字にシステム既定のフォントを使用する(Glitch Edition フレーバーのみに適用されます) -- cgit From 1b493c9fee954b5bd4c4b00f9f945a5d97e2d699 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 24 Jan 2022 19:06:19 +0100 Subject: Add optional hCaptcha support Fixes #1649 This requires setting `HCAPTCHA_SECRET_KEY` and `HCAPTCHA_SITE_KEY`, then enabling the admin setting at `/admin/settings/edit#form_admin_settings_captcha_enabled` Subsequently, a hCaptcha widget will be displayed on `/about` and `/auth/sign_up` unless: - the user is already signed-up already - the user has used an invite link - the user has already solved the captcha (and registration failed for another reason) The Content-Security-Policy headers are altered automatically to allow the third-party hCaptcha scripts on `/about` and `/auth/sign_up` following the same rules as above. --- .env.production.sample | 4 ++ Gemfile | 2 + Gemfile.lock | 3 ++ app/controllers/about_controller.rb | 2 + app/controllers/api/v1/accounts_controller.rb | 4 +- app/controllers/auth/registrations_controller.rb | 17 ++++++ app/controllers/concerns/captcha_concern.rb | 66 ++++++++++++++++++++++++ app/helpers/admin/settings_helper.rb | 4 ++ app/javascript/flavours/glitch/styles/forms.scss | 4 ++ app/models/form/admin_settings.rb | 2 + app/views/about/_registration.html.haml | 3 ++ app/views/admin/settings/edit.html.haml | 5 +- app/views/auth/registrations/new.html.haml | 3 ++ config/locales-glitch/en.yml | 3 ++ config/settings.yml | 1 + 15 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 app/controllers/concerns/captcha_concern.rb (limited to 'config/locales-glitch') diff --git a/.env.production.sample b/.env.production.sample index 13e89b40d..7de5e00f4 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -285,3 +285,7 @@ MAX_POLL_OPTION_CHARS=100 # Units are in bytes MAX_EMOJI_SIZE=51200 MAX_REMOTE_EMOJI_SIZE=204800 + +# Optional hCaptcha support +# HCAPTCHA_SECRET_KEY= +# HCAPTCHA_SITE_KEY= diff --git a/Gemfile b/Gemfile index eae5f11b7..282ab65e4 100644 --- a/Gemfile +++ b/Gemfile @@ -156,3 +156,5 @@ gem 'concurrent-ruby', require: false gem 'connection_pool', require: false gem 'xorcist', '~> 1.1' + +gem "hcaptcha", "~> 7.1" diff --git a/Gemfile.lock b/Gemfile.lock index 8d72732eb..cc9a53e41 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -271,6 +271,8 @@ GEM railties (>= 4.0.1) hashdiff (1.0.1) hashie (4.1.0) + hcaptcha (7.1.0) + json highline (2.0.3) hiredis (0.6.3) hkdf (0.3.0) @@ -719,6 +721,7 @@ DEPENDENCIES fog-openstack (~> 0.3) fuubar (~> 2.5) hamlit-rails (~> 0.2) + hcaptcha (~> 7.1) hiredis (~> 0.6) htmlentities (~> 4.3) http (~> 5.0) diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb index 620c0ff78..5a35dbbcb 100644 --- a/app/controllers/about_controller.rb +++ b/app/controllers/about_controller.rb @@ -2,6 +2,7 @@ class AboutController < ApplicationController include RegistrationSpamConcern + include CaptchaConcern before_action :set_pack @@ -12,6 +13,7 @@ class AboutController < ApplicationController before_action :set_instance_presenter before_action :set_expires_in, only: [:more, :terms] before_action :set_registration_form_time, only: :show + before_action :extend_csp_for_captcha!, only: :show skip_before_action :require_functional!, only: [:more, :terms] diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index 5c47158e0..8916c3f96 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Api::V1::AccountsController < Api::BaseController + include CaptchaConcern + before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :remove_from_followers, :block, :unblock, :mute, :unmute] before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, only: [:follow, :unfollow, :remove_from_followers] before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute] @@ -83,7 +85,7 @@ class Api::V1::AccountsController < Api::BaseController end def check_enabled_registrations - forbidden if single_user_mode? || omniauth_only? || !allowed_registrations? + forbidden if single_user_mode? || omniauth_only? || !allowed_registrations? || captcha_enabled? end def allowed_registrations? diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index 6b1f3fa82..3c9b38a4b 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -2,6 +2,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController include RegistrationSpamConcern + include CaptchaConcern layout :determine_layout @@ -15,6 +16,8 @@ class Auth::RegistrationsController < Devise::RegistrationsController before_action :require_not_suspended!, only: [:update] before_action :set_cache_headers, only: [:edit, :update] before_action :set_registration_form_time, only: :new + before_action :extend_csp_for_captcha!, only: [:new, :create] + before_action :check_captcha!, only: :create skip_before_action :require_functional!, only: [:edit, :update] @@ -135,4 +138,18 @@ class Auth::RegistrationsController < Devise::RegistrationsController def set_cache_headers response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate' end + + def sign_up(resource_name, resource) + clear_captcha! + super + end + + def check_captcha! + super do |error| + build_resource(sign_up_params) + resource.validate + resource.errors.add(:base, error) + respond_with resource + end + end end diff --git a/app/controllers/concerns/captcha_concern.rb b/app/controllers/concerns/captcha_concern.rb new file mode 100644 index 000000000..5a23e59e3 --- /dev/null +++ b/app/controllers/concerns/captcha_concern.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module CaptchaConcern + extend ActiveSupport::Concern + include Hcaptcha::Adapters::ViewMethods + + CAPTCHA_TIMEOUT = 2.hours.freeze + + included do + helper_method :render_captcha_if_needed + end + + def captcha_available? + ENV['HCAPTCHA_SECRET_KEY'].present? && ENV['HCAPTCHA_SITE_KEY'].present? + end + + def captcha_enabled? + captcha_available? && Setting.captcha_enabled + end + + def captcha_recently_passed? + session[:captcha_passed_at].present? && session[:captcha_passed_at] >= CAPTCHA_TIMEOUT.ago + end + + def captcha_required? + captcha_enabled? && !current_user && !(@invite.present? && @invite.valid_for_use? && !@invite.max_uses.nil?) && !captcha_recently_passed? + end + + def clear_captcha! + session.delete(:captcha_passed_at) + end + + def check_captcha! + return true unless captcha_required? + + if verify_hcaptcha + session[:captcha_passed_at] = Time.now.utc + return true + else + if block_given? + message = flash[:hcaptcha_error] + flash.delete(:hcaptcha_error) + yield message + end + return false + end + end + + def extend_csp_for_captcha! + policy = request.content_security_policy + return unless captcha_required? && policy.present? + + %w(script_src frame_src style_src connect_src).each do |directive| + values = policy.send(directive) + values << 'https://hcaptcha.com' unless values.include?('https://hcaptcha.com') || values.include?('https:') + values << 'https://*.hcaptcha.com' unless values.include?('https://*.hcaptcha.com') || values.include?('https:') + policy.send(directive, *values) + end + end + + def render_captcha_if_needed + return unless captcha_required? + + hcaptcha_tags + end +end diff --git a/app/helpers/admin/settings_helper.rb b/app/helpers/admin/settings_helper.rb index baf14ab25..f99a2b8c8 100644 --- a/app/helpers/admin/settings_helper.rb +++ b/app/helpers/admin/settings_helper.rb @@ -8,4 +8,8 @@ module Admin::SettingsHelper link = link_to t('admin.site_uploads.delete'), admin_site_upload_path(upload), data: { method: :delete } safe_join([hint, link], '
'.html_safe) end + + def captcha_available? + ENV['HCAPTCHA_SECRET_KEY'].present? && ENV['HCAPTCHA_SITE_KEY'].present? + end end diff --git a/app/javascript/flavours/glitch/styles/forms.scss b/app/javascript/flavours/glitch/styles/forms.scss index 3433abcdd..64d441fb2 100644 --- a/app/javascript/flavours/glitch/styles/forms.scss +++ b/app/javascript/flavours/glitch/styles/forms.scss @@ -1058,3 +1058,7 @@ code { display: none; } } + +.simple_form .h-captcha { + text-align: center; +} diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index 3202d1fc2..34f14e312 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -40,6 +40,7 @@ class Form::AdminSettings noindex outgoing_spoilers require_invite_text + captcha_enabled ).freeze BOOLEAN_KEYS = %i( @@ -58,6 +59,7 @@ class Form::AdminSettings trendable_by_default noindex require_invite_text + captcha_enabled ).freeze UPLOAD_KEYS = %i( diff --git a/app/views/about/_registration.html.haml b/app/views/about/_registration.html.haml index e4d614d71..5bb5d08a2 100644 --- a/app/views/about/_registration.html.haml +++ b/app/views/about/_registration.html.haml @@ -21,6 +21,9 @@ .fields-group = f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), required: true, disabled: closed_registrations? + .fields-group + = render_captcha_if_needed + .actions = f.button :button, sign_up_message, type: :submit, class: 'button button-primary', disabled: closed_registrations? diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml index b9daae8f0..49b03a9e3 100644 --- a/app/views/admin/settings/edit.html.haml +++ b/app/views/admin/settings/edit.html.haml @@ -42,7 +42,10 @@ .fields-group = f.input :require_invite_text, as: :boolean, wrapper: :with_label, label: t('admin.settings.registrations.require_invite_text.title'), hint: t('admin.settings.registrations.require_invite_text.desc_html'), disabled: !approved_registrations? - .fields-group + + - if captcha_available? + .fields-group + = f.input :captcha_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.captcha_enabled.title'), hint: t('admin.settings.captcha_enabled.desc_html') %hr.spacer/ diff --git a/app/views/auth/registrations/new.html.haml b/app/views/auth/registrations/new.html.haml index 6981195ed..5cb558297 100644 --- a/app/views/auth/registrations/new.html.haml +++ b/app/views/auth/registrations/new.html.haml @@ -38,6 +38,9 @@ .fields-group = f.input :agreement, as: :boolean, wrapper: :with_label, label: whitelist_mode? ? t('auth.checkbox_agreement_without_rules_html', terms_path: terms_path) : t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), required: true + .field-group + = render_captcha_if_needed + .actions = f.button :button, @invite.present? ? t('auth.register') : sign_up_message, type: :submit diff --git a/config/locales-glitch/en.yml b/config/locales-glitch/en.yml index 5cc2625fc..c96f21c92 100644 --- a/config/locales-glitch/en.yml +++ b/config/locales-glitch/en.yml @@ -2,6 +2,9 @@ en: admin: settings: + captcha_enabled: + desc_html: Enable hCaptcha integration, requiring new users to solve a challenge when signing up. Note that this disables app-based registration, and requires third-party scripts from hCaptcha to be embedded in the registration pages. This may have security and privacy concerns. + title: Require new users to go through a CAPTCHA to sign up enable_keybase: desc_html: Allow your users to prove their identity via keybase title: Enable keybase integration diff --git a/config/settings.yml b/config/settings.yml index 094209822..7d192f369 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -77,6 +77,7 @@ defaults: &defaults show_domain_blocks_rationale: 'disabled' outgoing_spoilers: '' require_invite_text: false + captcha_enabled: false development: <<: *defaults -- cgit From a9269f8786033eecf0ad307c75f5717c5ab468a2 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 25 Jan 2022 13:54:11 +0100 Subject: Disable `registrations` flag in /api/v1/instance when CAPTCHA is enabled This is to avoid apps trying and failing at using the registrations API, which does not let us require a CAPTCHA and cannot be clearly signaled as unavailable. --- app/serializers/rest/instance_serializer.rb | 6 +++++- config/locales-glitch/en.yml | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'config/locales-glitch') diff --git a/app/serializers/rest/instance_serializer.rb b/app/serializers/rest/instance_serializer.rb index 48bbb55c8..94cc3ffe3 100644 --- a/app/serializers/rest/instance_serializer.rb +++ b/app/serializers/rest/instance_serializer.rb @@ -98,7 +98,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer end def registrations - Setting.registrations_mode != 'none' && !Rails.configuration.x.single_user_mode + Setting.registrations_mode != 'none' && !Rails.configuration.x.single_user_mode && !captcha_enabled? end def approval_required @@ -114,4 +114,8 @@ class REST::InstanceSerializer < ActiveModel::Serializer def instance_presenter @instance_presenter ||= InstancePresenter.new end + + def captcha_enabled? + ENV['HCAPTCHA_SECRET_KEY'].present? && ENV['HCAPTCHA_SITE_KEY'].present? && Setting.captcha_enabled + end end diff --git a/config/locales-glitch/en.yml b/config/locales-glitch/en.yml index c96f21c92..9bd66c969 100644 --- a/config/locales-glitch/en.yml +++ b/config/locales-glitch/en.yml @@ -3,7 +3,7 @@ en: admin: settings: captcha_enabled: - desc_html: Enable hCaptcha integration, requiring new users to solve a challenge when signing up. Note that this disables app-based registration, and requires third-party scripts from hCaptcha to be embedded in the registration pages. This may have security and privacy concerns. + desc_html: Enable hCaptcha integration, requiring new users to solve a challenge when signing up. Note that this disables app-based registration, may prevent your instance from being listed as having open registrations, and requires third-party scripts from hCaptcha to be embedded in the registration pages. This may have security and privacy concerns. title: Require new users to go through a CAPTCHA to sign up enable_keybase: desc_html: Allow your users to prove their identity via keybase -- cgit From 0fb907441c827cadc767641b29d5d2c0e554f7a4 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 25 Jan 2022 22:37:12 +0100 Subject: Add ability to set hCaptcha either on registration form or on e-mail validation Upshot of CAPTCHA on e-mail validation is it does not need to break the in-band registration API. --- app/controllers/auth/confirmations_controller.rb | 50 ++++++++++++++++++++++++ app/controllers/concerns/captcha_concern.rb | 12 +++++- app/models/form/admin_settings.rb | 4 +- app/serializers/rest/instance_serializer.rb | 2 +- app/views/admin/settings/edit.html.haml | 2 +- app/views/auth/confirmations/captcha.html.haml | 11 ++++++ config/locales-glitch/en.yml | 17 ++++++-- config/routes.rb | 1 + config/settings.yml | 2 +- 9 files changed, 91 insertions(+), 10 deletions(-) create mode 100644 app/views/auth/confirmations/captcha.html.haml (limited to 'config/locales-glitch') diff --git a/app/controllers/auth/confirmations_controller.rb b/app/controllers/auth/confirmations_controller.rb index 0b5a2f3c9..e9a646f91 100644 --- a/app/controllers/auth/confirmations_controller.rb +++ b/app/controllers/auth/confirmations_controller.rb @@ -1,12 +1,18 @@ # frozen_string_literal: true class Auth::ConfirmationsController < Devise::ConfirmationsController + include CaptchaConcern + layout 'auth' before_action :set_body_classes before_action :set_pack + before_action :set_confirmation_user!, only: [:show, :confirm_captcha] before_action :require_unconfirmed! + before_action :extend_csp_for_captcha!, only: [:show, :confirm_captcha] + before_action :require_captcha_if_needed!, only: [:show] + skip_before_action :require_functional! def new @@ -15,8 +21,52 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController resource.email = current_user.unconfirmed_email || current_user.email if user_signed_in? end + def show + clear_captcha! + + old_session_values = session.to_hash + reset_session + session.update old_session_values.except('session_id') + + super + end + + def confirm_captcha + check_captcha! do |message| + flash.now[:alert] = message + render :captcha + return + end + + show + end + private + def require_captcha_if_needed! + render :captcha if captcha_required? + end + + def set_confirmation_user! + # We need to reimplement looking up the user because + # Devise::ConfirmationsController#show looks up and confirms in one + # step. + confirmation_token = params[:confirmation_token] + return if confirmation_token.nil? + @confirmation_user = User.find_first_by_auth_conditions(confirmation_token: confirmation_token) + end + + def captcha_user_bypass? + return true if @confirmation_user.nil? || @confirmation_user.confirmed? + + invite = Invite.find(@confirmation_user.invite_id) if @confirmation_user.invite_id.present? + invite.present? && !invite.max_uses.nil? + end + + def captcha_context + 'email-confirmation' + end + def set_pack use_pack 'auth' end diff --git a/app/controllers/concerns/captcha_concern.rb b/app/controllers/concerns/captcha_concern.rb index 4a942c988..02069d205 100644 --- a/app/controllers/concerns/captcha_concern.rb +++ b/app/controllers/concerns/captcha_concern.rb @@ -15,17 +15,21 @@ module CaptchaConcern end def captcha_enabled? - captcha_available? && Setting.captcha_enabled + captcha_available? && Setting.captcha_mode == captcha_context end def captcha_recently_passed? session[:captcha_passed_at].present? && session[:captcha_passed_at] >= CAPTCHA_TIMEOUT.ago end + def captcha_user_bypass? + current_user.present? || (@invite.present? && @invite.valid_for_use? && !@invite.max_uses.nil?) + end + def captcha_required? return false if ENV['OMNIAUTH_ONLY'] == 'true' return false unless Setting.registrations_mode != 'none' || @invite&.valid_for_use? - captcha_enabled? && !current_user && !(@invite.present? && @invite.valid_for_use? && !@invite.max_uses.nil?) && !captcha_recently_passed? + captcha_enabled? && !captcha_user_bypass? && !captcha_recently_passed? end def clear_captcha! @@ -65,4 +69,8 @@ module CaptchaConcern hcaptcha_tags end + + def captcha_context + 'registration-form' + end end diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index 34f14e312..7abb0d6c6 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -40,7 +40,7 @@ class Form::AdminSettings noindex outgoing_spoilers require_invite_text - captcha_enabled + captcha_mode ).freeze BOOLEAN_KEYS = %i( @@ -59,7 +59,6 @@ class Form::AdminSettings trendable_by_default noindex require_invite_text - captcha_enabled ).freeze UPLOAD_KEYS = %i( @@ -83,6 +82,7 @@ class Form::AdminSettings validates :bootstrap_timeline_accounts, existing_username: { multiple: true } validates :show_domain_blocks, inclusion: { in: %w(disabled users all) } validates :show_domain_blocks_rationale, inclusion: { in: %w(disabled users all) } + validates :captcha_mode, inclusion: { in: %w(disabled registration-form email-confirmation) } def initialize(_attributes = {}) super diff --git a/app/serializers/rest/instance_serializer.rb b/app/serializers/rest/instance_serializer.rb index 94cc3ffe3..d343cca20 100644 --- a/app/serializers/rest/instance_serializer.rb +++ b/app/serializers/rest/instance_serializer.rb @@ -116,6 +116,6 @@ class REST::InstanceSerializer < ActiveModel::Serializer end def captcha_enabled? - ENV['HCAPTCHA_SECRET_KEY'].present? && ENV['HCAPTCHA_SITE_KEY'].present? && Setting.captcha_enabled + ENV['HCAPTCHA_SECRET_KEY'].present? && ENV['HCAPTCHA_SITE_KEY'].present? && Setting.captcha_mode == 'registration-form' end end diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml index 49b03a9e3..fc042f845 100644 --- a/app/views/admin/settings/edit.html.haml +++ b/app/views/admin/settings/edit.html.haml @@ -45,7 +45,7 @@ - if captcha_available? .fields-group - = f.input :captcha_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.captcha_enabled.title'), hint: t('admin.settings.captcha_enabled.desc_html') + = f.input :captcha_mode, as: :radio_buttons, collection: %w(disabled registration-form email-confirmation), include_blank: false, wrapper: :with_block_label, label_method: ->(type) { safe_join([t("admin.settings.captcha.#{type}.title"), content_tag(:span, t("admin.settings.captcha.#{type}.desc_html"), class: 'hint')])}, label: t('admin.settings.captcha.title'), hint: t('admin.settings.captcha.desc_html') %hr.spacer/ diff --git a/app/views/auth/confirmations/captcha.html.haml b/app/views/auth/confirmations/captcha.html.haml new file mode 100644 index 000000000..850bc1479 --- /dev/null +++ b/app/views/auth/confirmations/captcha.html.haml @@ -0,0 +1,11 @@ +- content_for :page_title do + = t('auth.confirm_captcha') + += form_tag auth_captcha_confirmation_url, method: 'POST', class: 'simple_form' do + = hidden_field_tag :confirmation_token, params[:confirmation_token] + + .field-group + = render_captcha_if_needed + + .actions + %button.button= t('challenge.continue') diff --git a/config/locales-glitch/en.yml b/config/locales-glitch/en.yml index 9bd66c969..6ad5a5365 100644 --- a/config/locales-glitch/en.yml +++ b/config/locales-glitch/en.yml @@ -2,9 +2,18 @@ en: admin: settings: - captcha_enabled: - desc_html: Enable hCaptcha integration, requiring new users to solve a challenge when signing up. Note that this disables app-based registration, may prevent your instance from being listed as having open registrations, and requires third-party scripts from hCaptcha to be embedded in the registration pages. This may have security and privacy concerns. - title: Require new users to go through a CAPTCHA to sign up + captcha: + desc_html: Configure hCaptcha integration, relying on third-party scripts. This may have security and privacy implications. + email-confirmation: + desc_html: Require new users to go through hCaptcha at the e-mail validation step. Bots will not be deterred from creating accounts, but they may be prevented from confirming them, leaving them to be automatically cleaned up after a couple days. This does not interfere with app-based registrations. + title: CAPTCHA on email validation + disabled: + desc_html: Do not require a CAPTCHA + title: Disabled + registration-form: + desc_html: Require new users to go through hCaptcha on the registration form, so that CAPTCHA requirement is immediately apparent to them. This disables app-based registrations and may prevent your instance from being listed as having open registrations. + title: CAPTCHA on registration forms + title: CAPTCHA configuration enable_keybase: desc_html: Allow your users to prove their identity via keybase title: Enable keybase integration @@ -20,6 +29,8 @@ en: show_replies_in_public_timelines: desc_html: In addition to public self-replies (threads), show public replies in local and public timelines. title: Show replies in public timelines + auth: + confirm_captcha: User verification generic: use_this: Use this settings: diff --git a/config/routes.rb b/config/routes.rb index 65dd7ad63..d0eeda1e8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -44,6 +44,7 @@ Rails.application.routes.draw do resource :setup, only: [:show, :update], controller: :setup resource :challenge, only: [:create], controller: :challenges get 'sessions/security_key_options', to: 'sessions#webauthn_options' + post 'captcha_confirmation', to: 'confirmations#confirm_captcha', as: :captcha_confirmation end end diff --git a/config/settings.yml b/config/settings.yml index 7d192f369..b5437caee 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -77,7 +77,7 @@ defaults: &defaults show_domain_blocks_rationale: 'disabled' outgoing_spoilers: '' require_invite_text: false - captcha_enabled: false + captcha_mode: 'disabled' development: <<: *defaults -- cgit From b7cf3941b3783220e6b3bc9a6d3975ceecdc64cb Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 25 Jan 2022 23:56:57 +0100 Subject: Change CAPTCHA handling to be only on email verification This simplifies the implementation considerably, and while not providing ideal UX, it's the most flexible approach. --- app/controllers/about_controller.rb | 2 -- app/controllers/api/v1/accounts_controller.rb | 4 +--- app/controllers/auth/confirmations_controller.rb | 6 ------ app/controllers/auth/registrations_controller.rb | 22 ------------------- app/controllers/concerns/captcha_concern.rb | 27 +++++------------------- app/models/form/admin_settings.rb | 4 ++-- app/serializers/rest/instance_serializer.rb | 6 +----- app/views/about/_registration.html.haml | 3 --- app/views/admin/settings/edit.html.haml | 2 +- app/views/auth/confirmations/captcha.html.haml | 2 +- app/views/auth/registrations/new.html.haml | 3 --- config/locales-glitch/en.yml | 15 +++---------- config/settings.yml | 2 +- spec/views/about/show.html.haml_spec.rb | 1 - 14 files changed, 15 insertions(+), 84 deletions(-) (limited to 'config/locales-glitch') diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb index 5a35dbbcb..620c0ff78 100644 --- a/app/controllers/about_controller.rb +++ b/app/controllers/about_controller.rb @@ -2,7 +2,6 @@ class AboutController < ApplicationController include RegistrationSpamConcern - include CaptchaConcern before_action :set_pack @@ -13,7 +12,6 @@ class AboutController < ApplicationController before_action :set_instance_presenter before_action :set_expires_in, only: [:more, :terms] before_action :set_registration_form_time, only: :show - before_action :extend_csp_for_captcha!, only: :show skip_before_action :require_functional!, only: [:more, :terms] diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index 8916c3f96..5c47158e0 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true class Api::V1::AccountsController < Api::BaseController - include CaptchaConcern - before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :remove_from_followers, :block, :unblock, :mute, :unmute] before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, only: [:follow, :unfollow, :remove_from_followers] before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute] @@ -85,7 +83,7 @@ class Api::V1::AccountsController < Api::BaseController end def check_enabled_registrations - forbidden if single_user_mode? || omniauth_only? || !allowed_registrations? || captcha_enabled? + forbidden if single_user_mode? || omniauth_only? || !allowed_registrations? end def allowed_registrations? diff --git a/app/controllers/auth/confirmations_controller.rb b/app/controllers/auth/confirmations_controller.rb index e9a646f91..17ad56fa8 100644 --- a/app/controllers/auth/confirmations_controller.rb +++ b/app/controllers/auth/confirmations_controller.rb @@ -22,8 +22,6 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController end def show - clear_captcha! - old_session_values = session.to_hash reset_session session.update old_session_values.except('session_id') @@ -63,10 +61,6 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController invite.present? && !invite.max_uses.nil? end - def captcha_context - 'email-confirmation' - end - def set_pack use_pack 'auth' end diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index 0db9cb84d..6b1f3fa82 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -2,7 +2,6 @@ class Auth::RegistrationsController < Devise::RegistrationsController include RegistrationSpamConcern - include CaptchaConcern layout :determine_layout @@ -16,8 +15,6 @@ class Auth::RegistrationsController < Devise::RegistrationsController before_action :require_not_suspended!, only: [:update] before_action :set_cache_headers, only: [:edit, :update] before_action :set_registration_form_time, only: :new - before_action :extend_csp_for_captcha!, only: [:new, :create] - before_action :check_captcha!, only: :create skip_before_action :require_functional!, only: [:edit, :update] @@ -138,23 +135,4 @@ class Auth::RegistrationsController < Devise::RegistrationsController def set_cache_headers response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate' end - - def sign_up(resource_name, resource) - clear_captcha! - - old_session_values = session.to_hash - reset_session - session.update old_session_values.except('session_id') - - super - end - - def check_captcha! - super do |error| - build_resource(sign_up_params) - resource.validate - resource.errors.add(:base, error) - respond_with resource - end - end end diff --git a/app/controllers/concerns/captcha_concern.rb b/app/controllers/concerns/captcha_concern.rb index 02069d205..538c1ffb1 100644 --- a/app/controllers/concerns/captcha_concern.rb +++ b/app/controllers/concerns/captcha_concern.rb @@ -4,10 +4,8 @@ module CaptchaConcern extend ActiveSupport::Concern include Hcaptcha::Adapters::ViewMethods - CAPTCHA_TIMEOUT = 2.hours.freeze - included do - helper_method :render_captcha_if_needed + helper_method :render_captcha end def captcha_available? @@ -15,32 +13,21 @@ module CaptchaConcern end def captcha_enabled? - captcha_available? && Setting.captcha_mode == captcha_context - end - - def captcha_recently_passed? - session[:captcha_passed_at].present? && session[:captcha_passed_at] >= CAPTCHA_TIMEOUT.ago + captcha_available? && Setting.captcha_enabled end def captcha_user_bypass? - current_user.present? || (@invite.present? && @invite.valid_for_use? && !@invite.max_uses.nil?) + false end def captcha_required? - return false if ENV['OMNIAUTH_ONLY'] == 'true' - return false unless Setting.registrations_mode != 'none' || @invite&.valid_for_use? - captcha_enabled? && !captcha_user_bypass? && !captcha_recently_passed? - end - - def clear_captcha! - session.delete(:captcha_passed_at) + captcha_enabled? && !captcha_user_bypass? end def check_captcha! return true unless captcha_required? if verify_hcaptcha - session[:captcha_passed_at] = Time.now.utc true else if block_given? @@ -64,13 +51,9 @@ module CaptchaConcern end end - def render_captcha_if_needed + def render_captcha return unless captcha_required? hcaptcha_tags end - - def captcha_context - 'registration-form' - end end diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index 7abb0d6c6..34f14e312 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -40,7 +40,7 @@ class Form::AdminSettings noindex outgoing_spoilers require_invite_text - captcha_mode + captcha_enabled ).freeze BOOLEAN_KEYS = %i( @@ -59,6 +59,7 @@ class Form::AdminSettings trendable_by_default noindex require_invite_text + captcha_enabled ).freeze UPLOAD_KEYS = %i( @@ -82,7 +83,6 @@ class Form::AdminSettings validates :bootstrap_timeline_accounts, existing_username: { multiple: true } validates :show_domain_blocks, inclusion: { in: %w(disabled users all) } validates :show_domain_blocks_rationale, inclusion: { in: %w(disabled users all) } - validates :captcha_mode, inclusion: { in: %w(disabled registration-form email-confirmation) } def initialize(_attributes = {}) super diff --git a/app/serializers/rest/instance_serializer.rb b/app/serializers/rest/instance_serializer.rb index d343cca20..48bbb55c8 100644 --- a/app/serializers/rest/instance_serializer.rb +++ b/app/serializers/rest/instance_serializer.rb @@ -98,7 +98,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer end def registrations - Setting.registrations_mode != 'none' && !Rails.configuration.x.single_user_mode && !captcha_enabled? + Setting.registrations_mode != 'none' && !Rails.configuration.x.single_user_mode end def approval_required @@ -114,8 +114,4 @@ class REST::InstanceSerializer < ActiveModel::Serializer def instance_presenter @instance_presenter ||= InstancePresenter.new end - - def captcha_enabled? - ENV['HCAPTCHA_SECRET_KEY'].present? && ENV['HCAPTCHA_SITE_KEY'].present? && Setting.captcha_mode == 'registration-form' - end end diff --git a/app/views/about/_registration.html.haml b/app/views/about/_registration.html.haml index 5bb5d08a2..e4d614d71 100644 --- a/app/views/about/_registration.html.haml +++ b/app/views/about/_registration.html.haml @@ -21,9 +21,6 @@ .fields-group = f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), required: true, disabled: closed_registrations? - .fields-group - = render_captcha_if_needed - .actions = f.button :button, sign_up_message, type: :submit, class: 'button button-primary', disabled: closed_registrations? diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml index fc042f845..49b03a9e3 100644 --- a/app/views/admin/settings/edit.html.haml +++ b/app/views/admin/settings/edit.html.haml @@ -45,7 +45,7 @@ - if captcha_available? .fields-group - = f.input :captcha_mode, as: :radio_buttons, collection: %w(disabled registration-form email-confirmation), include_blank: false, wrapper: :with_block_label, label_method: ->(type) { safe_join([t("admin.settings.captcha.#{type}.title"), content_tag(:span, t("admin.settings.captcha.#{type}.desc_html"), class: 'hint')])}, label: t('admin.settings.captcha.title'), hint: t('admin.settings.captcha.desc_html') + = f.input :captcha_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.captcha_enabled.title'), hint: t('admin.settings.captcha_enabled.desc_html') %hr.spacer/ diff --git a/app/views/auth/confirmations/captcha.html.haml b/app/views/auth/confirmations/captcha.html.haml index 850bc1479..0f7cf9c59 100644 --- a/app/views/auth/confirmations/captcha.html.haml +++ b/app/views/auth/confirmations/captcha.html.haml @@ -5,7 +5,7 @@ = hidden_field_tag :confirmation_token, params[:confirmation_token] .field-group - = render_captcha_if_needed + = render_captcha .actions %button.button= t('challenge.continue') diff --git a/app/views/auth/registrations/new.html.haml b/app/views/auth/registrations/new.html.haml index 5cb558297..6981195ed 100644 --- a/app/views/auth/registrations/new.html.haml +++ b/app/views/auth/registrations/new.html.haml @@ -38,9 +38,6 @@ .fields-group = f.input :agreement, as: :boolean, wrapper: :with_label, label: whitelist_mode? ? t('auth.checkbox_agreement_without_rules_html', terms_path: terms_path) : t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), required: true - .field-group - = render_captcha_if_needed - .actions = f.button :button, @invite.present? ? t('auth.register') : sign_up_message, type: :submit diff --git a/config/locales-glitch/en.yml b/config/locales-glitch/en.yml index 6ad5a5365..ab7f1b976 100644 --- a/config/locales-glitch/en.yml +++ b/config/locales-glitch/en.yml @@ -2,18 +2,9 @@ en: admin: settings: - captcha: - desc_html: Configure hCaptcha integration, relying on third-party scripts. This may have security and privacy implications. - email-confirmation: - desc_html: Require new users to go through hCaptcha at the e-mail validation step. Bots will not be deterred from creating accounts, but they may be prevented from confirming them, leaving them to be automatically cleaned up after a couple days. This does not interfere with app-based registrations. - title: CAPTCHA on email validation - disabled: - desc_html: Do not require a CAPTCHA - title: Disabled - registration-form: - desc_html: Require new users to go through hCaptcha on the registration form, so that CAPTCHA requirement is immediately apparent to them. This disables app-based registrations and may prevent your instance from being listed as having open registrations. - title: CAPTCHA on registration forms - title: CAPTCHA configuration + captcha_enabled: + desc_html: Enable hCaptcha integration, requiring new users to solve a challenge when confirming their email address. This requires third-party scripts from hCaptcha to be embedded in the email verification page, which may have security and privacy concerns. Users that have been invited through a limited-use invite will not need to solve a CAPTCHA challenge. + title: Require new users to go through a CAPTCHA to confirm their account enable_keybase: desc_html: Allow your users to prove their identity via keybase title: Enable keybase integration diff --git a/config/settings.yml b/config/settings.yml index b5437caee..7d192f369 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -77,7 +77,7 @@ defaults: &defaults show_domain_blocks_rationale: 'disabled' outgoing_spoilers: '' require_invite_text: false - captcha_mode: 'disabled' + captcha_enabled: false development: <<: *defaults diff --git a/spec/views/about/show.html.haml_spec.rb b/spec/views/about/show.html.haml_spec.rb index 12c96ea49..d608bbf5d 100644 --- a/spec/views/about/show.html.haml_spec.rb +++ b/spec/views/about/show.html.haml_spec.rb @@ -10,7 +10,6 @@ describe 'about/show.html.haml', without_verify_partial_doubles: true do allow(view).to receive(:site_title).and_return('example site') allow(view).to receive(:new_user).and_return(User.new) allow(view).to receive(:use_seamless_external_login?).and_return(false) - allow(view).to receive(:render_captcha_if_needed).and_return(nil) end it 'has valid open graph tags' do -- cgit From f997a5463b2e81e2eb050d97fc2fbbb252ab0b58 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 26 Jan 2022 11:13:38 +0100 Subject: Add mention of accessibility issues to hCaptcha option in admin page --- config/locales-glitch/en.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'config/locales-glitch') diff --git a/config/locales-glitch/en.yml b/config/locales-glitch/en.yml index ab7f1b976..0d799b517 100644 --- a/config/locales-glitch/en.yml +++ b/config/locales-glitch/en.yml @@ -3,8 +3,8 @@ en: admin: settings: captcha_enabled: - desc_html: Enable hCaptcha integration, requiring new users to solve a challenge when confirming their email address. This requires third-party scripts from hCaptcha to be embedded in the email verification page, which may have security and privacy concerns. Users that have been invited through a limited-use invite will not need to solve a CAPTCHA challenge. - title: Require new users to go through a CAPTCHA to confirm their account + desc_html: This relies on external scripts from hCaptcha, which may be a security and privacy concern. In addition, this can make the registration process significantly less accessible to some (especially disabled) people. For these reasons, please consider alternative measures such as approval-based or invite-based registration.
Users that have been invited through a limited-use invite will not need to solve a CAPTCHA + title: Require new users to solve a CAPTCHA to confirm their account enable_keybase: desc_html: Allow your users to prove their identity via keybase title: Enable keybase integration -- cgit From 7c2204314ab087ef3f1102f89d0d74863cfcaf8f Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 26 Jan 2022 13:24:51 +0100 Subject: Add some explanation text on the CAPTCHA confirmation page --- app/views/auth/confirmations/captcha.html.haml | 5 ++++- config/locales-glitch/en.yml | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'config/locales-glitch') diff --git a/app/views/auth/confirmations/captcha.html.haml b/app/views/auth/confirmations/captcha.html.haml index 0f7cf9c59..0fae367db 100644 --- a/app/views/auth/confirmations/captcha.html.haml +++ b/app/views/auth/confirmations/captcha.html.haml @@ -1,9 +1,12 @@ - content_for :page_title do - = t('auth.confirm_captcha') + = t('auth.captcha_confirmation.title') = form_tag auth_captcha_confirmation_url, method: 'POST', class: 'simple_form' do = hidden_field_tag :confirmation_token, params[:confirmation_token] + .field-group + %p.hint= t('auth.captcha_confirmation.hint_html') + .field-group = render_captcha diff --git a/config/locales-glitch/en.yml b/config/locales-glitch/en.yml index 0d799b517..a654d9d13 100644 --- a/config/locales-glitch/en.yml +++ b/config/locales-glitch/en.yml @@ -21,7 +21,9 @@ en: desc_html: In addition to public self-replies (threads), show public replies in local and public timelines. title: Show replies in public timelines auth: - confirm_captcha: User verification + captcha_confirmation: + hint_html: Just one more step! To confirm your account, this server requires you to solve a CAPTCHA. Contact the server administrator if you have questions or need assistance with confirming your account. + title: User verification generic: use_this: Use this settings: -- cgit From b768a4dea94cb27fb43d4ae8b18ed195093eb4c6 Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 26 Jan 2022 14:09:11 +0100 Subject: Add link to /about/more to the CAPTCHA verification page --- config/locales-glitch/en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'config/locales-glitch') diff --git a/config/locales-glitch/en.yml b/config/locales-glitch/en.yml index a654d9d13..c382ee9ed 100644 --- a/config/locales-glitch/en.yml +++ b/config/locales-glitch/en.yml @@ -22,7 +22,7 @@ en: title: Show replies in public timelines auth: captcha_confirmation: - hint_html: Just one more step! To confirm your account, this server requires you to solve a CAPTCHA. Contact the server administrator if you have questions or need assistance with confirming your account. + hint_html: Just one more step! To confirm your account, this server requires you to solve a CAPTCHA. You can contact the server administrator if you have questions or need assistance with confirming your account. title: User verification generic: use_this: Use this -- cgit