diff --git a/frontend/src/components/dialog/generate-share-link.js b/frontend/src/components/dialog/generate-share-link.js index 0cde7f8f29..80584bca74 100644 --- a/frontend/src/components/dialog/generate-share-link.js +++ b/frontend/src/components/dialog/generate-share-link.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import moment from 'moment'; import copy from 'copy-to-clipboard'; import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, InputGroupText, Alert, FormText } from 'reactstrap'; -import { isPro, gettext, shareLinkExpireDaysMin, shareLinkExpireDaysMax, shareLinkExpireDaysDefault, shareLinkPasswordMinLength, canSendShareLinkEmail } from '../../utils/constants'; +import { isPro, gettext, shareLinkExpireDaysMin, shareLinkExpireDaysMax, shareLinkExpireDaysDefault, shareLinkForceUsePassword, shareLinkPasswordMinLength, shareLinkPasswordStrengthLevel, canSendShareLinkEmail } from '../../utils/constants'; import ShareLinkPermissionEditor from '../../components/select-editor/share-link-permission-editor'; import { seafileAPI } from '../../utils/seafile-api'; import { Utils } from '../../utils/utils'; @@ -47,7 +47,7 @@ class GenerateShareLink extends React.Component { this.state = { isOpIconShown: false, isValidate: false, - isShowPasswordInput: false, + isShowPasswordInput: shareLinkForceUsePassword ? true : false, isPasswordVisible: false, isExpireChecked: !this.isExpireDaysNoLimit, setExp: 'by-days', @@ -232,8 +232,8 @@ class GenerateShareLink extends React.Component { seafileAPI.deleteShareLink(sharedLinkInfo.token).then(() => { this.setState({ password: '', - passwordnew: '', - isShowPasswordInput: false, + passwdnew: '', + isShowPasswordInput: shareLinkForceUsePassword ? true : false, expireDays: this.defaultExpireDays, isExpireChecked: !this.isExpireDaysNoLimit, errorInfo: '', @@ -272,6 +272,10 @@ class GenerateShareLink extends React.Component { this.setState({errorInfo: 'Passwords don\'t match'}); return false; } + if (Utils.getStrengthLevel(password) < shareLinkPasswordStrengthLevel) { + this.setState({errorInfo: gettext('Password is too weak, should have at least {shareLinkPasswordStrengthLevel} of the following: num, upper letter, lower letter and other symbols'.replace('{shareLinkPasswordStrengthLevel}', shareLinkPasswordStrengthLevel))}); + return false; + } } if (isExpireChecked) { @@ -359,8 +363,9 @@ class GenerateShareLink extends React.Component { return ; } - let passwordLengthTip = gettext('(at least {passwordLength} characters)'); - passwordLengthTip = passwordLengthTip.replace('{passwordLength}', shareLinkPasswordMinLength); + let passwordLengthTip = gettext('(at least {passwordLength} characters and has {shareLinkPasswordStrengthLevel} of the following: num, upper letter, lower letter and other symbols)'); + passwordLengthTip = passwordLengthTip.replace('{passwordLength}', shareLinkPasswordMinLength) + .replace('{shareLinkPasswordStrengthLevel}', shareLinkPasswordStrengthLevel); if (this.state.sharedLinkInfo) { let sharedLinkInfo = this.state.sharedLinkInfo; @@ -442,10 +447,17 @@ class GenerateShareLink extends React.Component { return (
+ {shareLinkForceUsePassword ? ( + + ) : ( + )} {this.state.isShowPasswordInput &&
diff --git a/frontend/src/components/dialog/generate-upload-link.js b/frontend/src/components/dialog/generate-upload-link.js index 50da663a97..4d75783ff3 100644 --- a/frontend/src/components/dialog/generate-upload-link.js +++ b/frontend/src/components/dialog/generate-upload-link.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import copy from 'copy-to-clipboard'; import moment from 'moment'; import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, InputGroupText, Alert, FormText } from 'reactstrap'; -import { gettext, shareLinkPasswordMinLength, canSendShareLinkEmail, uploadLinkExpireDaysMin, uploadLinkExpireDaysMax, uploadLinkExpireDaysDefault } from '../../utils/constants'; +import { gettext, shareLinkForceUsePassword, shareLinkPasswordMinLength, shareLinkPasswordStrengthLevel, canSendShareLinkEmail, uploadLinkExpireDaysMin, uploadLinkExpireDaysMax, uploadLinkExpireDaysDefault } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; import { Utils } from '../../utils/utils'; import UploadLink from '../../models/upload-link'; @@ -42,10 +42,10 @@ class GenerateUploadLink extends React.Component { this.expirationLimitTip = expirationLimitTip; this.state = { - showPasswordInput: false, + showPasswordInput: shareLinkForceUsePassword ? true : false, passwordVisible: false, password: '', - passwdnew: '', + passwordnew: '', sharedUploadInfo: null, isSendLinkShown: false, isExpireChecked: !this.isExpireDaysNoLimit, @@ -80,7 +80,7 @@ class GenerateUploadLink extends React.Component { this.setState({ showPasswordInput: !this.state.showPasswordInput, password: '', - passwdnew: '', + passwordnew: '', errorInfo: '' }); } @@ -155,6 +155,10 @@ class GenerateUploadLink extends React.Component { this.setState({errorInfo: gettext('Passwords don\'t match')}); return false; } + if (Utils.getStrengthLevel(password) < shareLinkPasswordStrengthLevel) { + this.setState({errorInfo: gettext('Password is too weak, should have at least {shareLinkPasswordStrengthLevel} of the following: num, upper letter, lower letter and other symbols'.replace('{shareLinkPasswordStrengthLevel}', shareLinkPasswordStrengthLevel))}); + return false; + } } if (isExpireChecked) { @@ -233,7 +237,7 @@ class GenerateUploadLink extends React.Component { let sharedUploadInfo = this.state.sharedUploadInfo; seafileAPI.deleteUploadLink(sharedUploadInfo.token).then(() => { this.setState({ - showPasswordInput: false, + showPasswordInput: shareLinkForceUsePassword ? true : false, expireDays: this.defaultExpireDays, isExpireChecked: !this.isExpireDaysNoLimit, password: '', @@ -256,8 +260,10 @@ class GenerateUploadLink extends React.Component { const { isSendLinkShown } = this.state; - let passwordLengthTip = gettext('(at least {passwordLength} characters)'); - passwordLengthTip = passwordLengthTip.replace('{passwordLength}', shareLinkPasswordMinLength); + let passwordLengthTip = gettext('(at least {passwordLength} characters and has {shareLinkPasswordStrengthLevel} of the following: num, upper letter, lower letter and other symbols)'); + passwordLengthTip = passwordLengthTip.replace('{passwordLength}', shareLinkPasswordMinLength) + .replace('{shareLinkPasswordStrengthLevel}', shareLinkPasswordStrengthLevel); + if (this.state.sharedUploadInfo) { let sharedUploadInfo = this.state.sharedUploadInfo; return ( @@ -297,8 +303,17 @@ class GenerateUploadLink extends React.Component { {this.state.showPasswordInput &&
diff --git a/frontend/src/pages/sys-admin/web-settings/web-settings.js b/frontend/src/pages/sys-admin/web-settings/web-settings.js index 09e2bc1c9d..053f5102f4 100644 --- a/frontend/src/pages/sys-admin/web-settings/web-settings.js +++ b/frontend/src/pages/sys-admin/web-settings/web-settings.js @@ -299,12 +299,26 @@ class WebSettings extends Component { value={config_dict['REPO_PASSWORD_MIN_LENGTH']} helpTip={gettext('The least number of characters an encrypted library password should include.')} /> + + = 48 && n <= 57) // nums + return 1; + if (n >= 65 && n <= 90) // uppers + return 2; + if (n >= 97 && n <= 122) // lowers + return 4; + else + return 8; + }, + + calculateBitwise: function(num) { + var level = 0; + for (var i = 0; i < 4; i++){ + // bitwise AND + if (num&1) level++; + // Right logical shift + num>>>=1; + } + return level; + }, }; diff --git a/seahub/api2/endpoints/admin/web_settings.py b/seahub/api2/endpoints/admin/web_settings.py index df5883875d..c3da1e8ac4 100644 --- a/seahub/api2/endpoints/admin/web_settings.py +++ b/seahub/api2/endpoints/admin/web_settings.py @@ -4,7 +4,6 @@ import logging from constance import config from django.conf import settings as dj_settings -from django.utils.translation import ugettext as _ from rest_framework.authentication import SessionAuthentication from rest_framework.permissions import IsAdminUser @@ -25,6 +24,7 @@ DIGIT_WEB_SETTINGS = [ 'ENABLE_REPO_HISTORY_SETTING', 'USER_STRONG_PASSWORD_REQUIRED', 'ENABLE_ENCRYPTED_LIBRARY', 'USER_PASSWORD_MIN_LENGTH', 'USER_PASSWORD_STRENGTH_LEVEL', 'SHARE_LINK_PASSWORD_MIN_LENGTH', + 'SHARE_LINK_FORCE_USE_PASSWORD', 'SHARE_LINK_PASSWORD_STRENGTH_LEVEL', 'ENABLE_USER_CREATE_ORG_REPO', 'FORCE_PASSWORD_CHANGE', 'LOGIN_ATTEMPT_LIMIT', 'FREEZE_USER_ON_LOGIN_FAILED', 'ENABLE_SHARE_TO_ALL_GROUPS', 'ENABLE_TWO_FACTOR_AUTH', @@ -87,6 +87,10 @@ class AdminWebSettings(APIView): error_msg = 'value invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + if key == 'SHARE_LINK_PASSWORD_STRENGTH_LEVEL' and value not in (1, 2, 3, 4): + error_msg = 'value invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + if (key in STRING_WEB_SETTINGS and key != 'CUSTOM_CSS') and not value: error_msg = 'value invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) diff --git a/seahub/api2/endpoints/share_links.py b/seahub/api2/endpoints/share_links.py index 5a6c241be8..fc6a069c4a 100644 --- a/seahub/api2/endpoints/share_links.py +++ b/seahub/api2/endpoints/share_links.py @@ -32,7 +32,7 @@ from seahub.constants import PERMISSION_READ_WRITE, PERMISSION_READ, \ from seahub.share.models import FileShare, UploadLinkShare, check_share_link_access from seahub.utils import gen_shared_link, is_org_context, normalize_file_path, \ normalize_dir_path, is_pro_version, get_file_type_and_ext, \ - check_filename_with_rename, gen_file_upload_url + check_filename_with_rename, gen_file_upload_url, get_password_strength_level from seahub.utils.file_op import if_locked_by_online_office from seahub.utils.file_types import IMAGE, VIDEO, XMIND from seahub.utils.timeutils import datetime_to_isoformat_timestr, \ @@ -263,10 +263,21 @@ class ShareLinks(APIView): return api_error(status.HTTP_400_BAD_REQUEST, error_msg) password = request.data.get('password', None) - if password and len(password) < config.SHARE_LINK_PASSWORD_MIN_LENGTH: - error_msg = _('Password is too short.') + + if config.SHARE_LINK_FORCE_USE_PASSWORD and not password: + error_msg = _('Password is required.') return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + if password: + + if len(password) < config.SHARE_LINK_PASSWORD_MIN_LENGTH: + error_msg = _('Password is too short.') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if get_password_strength_level(password) < config.SHARE_LINK_PASSWORD_STRENGTH_LEVEL: + error_msg = _('Password is too weak.') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + expire_days = request.data.get('expire_days', '') expiration_time = request.data.get('expiration_time', '') if expire_days and expiration_time: diff --git a/seahub/api2/endpoints/upload_links.py b/seahub/api2/endpoints/upload_links.py index 185fc30597..4357246a00 100644 --- a/seahub/api2/endpoints/upload_links.py +++ b/seahub/api2/endpoints/upload_links.py @@ -26,7 +26,7 @@ from seahub.api2.permissions import CanGenerateUploadLink from seahub.share.models import UploadLinkShare, check_share_link_common from seahub.utils import gen_shared_upload_link, gen_file_upload_url, \ - is_pro_version + is_pro_version, get_password_strength_level from seahub.views import check_folder_permission from seahub.utils.timeutils import datetime_to_isoformat_timestr @@ -157,10 +157,21 @@ class UploadLinks(APIView): return api_error(status.HTTP_400_BAD_REQUEST, error_msg) password = request.data.get('password', None) - if password and len(password) < config.SHARE_LINK_PASSWORD_MIN_LENGTH: - error_msg = _('Password is too short') + + if config.SHARE_LINK_FORCE_USE_PASSWORD and not password: + error_msg = _('Password is required.') return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + if password: + + if len(password) < config.SHARE_LINK_PASSWORD_MIN_LENGTH: + error_msg = _('Password is too short.') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if get_password_strength_level(password) < config.SHARE_LINK_PASSWORD_STRENGTH_LEVEL: + error_msg = _('Password is too weak.') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + expire_days = request.data.get('expire_days', '') expiration_time = request.data.get('expiration_time', '') if expire_days and expiration_time: diff --git a/seahub/api2/views.py b/seahub/api2/views.py index 25f6dcd433..890b513afd 100644 --- a/seahub/api2/views.py +++ b/seahub/api2/views.py @@ -3996,7 +3996,7 @@ class SharedFileDetailView(APIView): password = request.GET.get('password', '') if not password: - return api_error(status.HTTP_403_FORBIDDEN, "Password is required") + return api_error(status.HTTP_403_FORBIDDEN, _('Password is required.')) if not check_password(password, fileshare.password): return api_error(status.HTTP_403_FORBIDDEN, "Invalid Password") @@ -4116,7 +4116,7 @@ class SharedDirView(APIView): password = request.GET.get('password', '') if not password: - return api_error(status.HTTP_403_FORBIDDEN, "Password is required") + return api_error(status.HTTP_403_FORBIDDEN, _('Password is required.')) if not check_password(password, fileshare.password): return api_error(status.HTTP_403_FORBIDDEN, "Invalid Password") diff --git a/seahub/base/context_processors.py b/seahub/base/context_processors.py index c79d589bb5..c950139acd 100644 --- a/seahub/base/context_processors.py +++ b/seahub/base/context_processors.py @@ -15,7 +15,7 @@ from django.conf import settings as dj_settings from django.utils.functional import lazy from constance import config -from seahub.settings import SEAFILE_VERSION, SITE_TITLE, SITE_NAME, \ +from seahub.settings import SEAFILE_VERSION, \ MAX_FILE_NAME, LOGO_PATH, BRANDING_CSS, LOGO_WIDTH, LOGO_HEIGHT,\ SHOW_REPO_DOWNLOAD_BUTTON, SITE_ROOT, ENABLE_GUEST_INVITATION, \ FAVICON_PATH, ENABLE_THUMBNAIL, THUMBNAIL_SIZE_FOR_ORIGINAL, \ @@ -121,7 +121,9 @@ def base(request): 'max_file_name': MAX_FILE_NAME, 'has_file_search': HAS_FILE_SEARCH, 'show_repo_download_button': SHOW_REPO_DOWNLOAD_BUTTON, + 'share_link_force_use_password': config.SHARE_LINK_FORCE_USE_PASSWORD, 'share_link_password_min_length': config.SHARE_LINK_PASSWORD_MIN_LENGTH, + 'share_link_password_strength_level': config.SHARE_LINK_PASSWORD_STRENGTH_LEVEL, 'repo_password_min_length': config.REPO_PASSWORD_MIN_LENGTH, 'events_enabled': EVENTS_ENABLED, 'sysadmin_extra_enabled': ENABLE_SYSADMIN_EXTRA, diff --git a/seahub/settings.py b/seahub/settings.py index 72b081ad9e..428a6e108b 100644 --- a/seahub/settings.py +++ b/seahub/settings.py @@ -353,8 +353,17 @@ UPLOAD_LINK_EXPIRE_DAYS_MAX = 0 # 0 means no limit # greater than or equal to MIN and less than or equal to MAX UPLOAD_LINK_EXPIRE_DAYS_DEFAULT = 0 -# mininum length for the password of a share link -SHARE_LINK_PASSWORD_MIN_LENGTH = 8 +# force use password when generate a share/upload link +SHARE_LINK_FORCE_USE_PASSWORD = False + +# mininum length for the password of a share/upload link +SHARE_LINK_PASSWORD_MIN_LENGTH = 10 + +# LEVEL for the password of a share/upload link +# based on four types of input: +# num, upper letter, lower letter, other symbols +# '3' means password must have at least 3 types of the above. +SHARE_LINK_PASSWORD_STRENGTH_LEVEL = 1 # enable or disable share link audit ENABLE_SHARE_LINK_AUDIT = False @@ -901,7 +910,9 @@ CONSTANCE_CONFIG = { 'USER_PASSWORD_STRENGTH_LEVEL': (USER_PASSWORD_STRENGTH_LEVEL, ''), 'SHARE_LINK_TOKEN_LENGTH': (SHARE_LINK_TOKEN_LENGTH, ''), + 'SHARE_LINK_FORCE_USE_PASSWORD': (SHARE_LINK_FORCE_USE_PASSWORD, ''), 'SHARE_LINK_PASSWORD_MIN_LENGTH': (SHARE_LINK_PASSWORD_MIN_LENGTH, ''), + 'SHARE_LINK_PASSWORD_STRENGTH_LEVEL': (SHARE_LINK_PASSWORD_STRENGTH_LEVEL, ''), 'ENABLE_TWO_FACTOR_AUTH': (ENABLE_TWO_FACTOR_AUTH, ''), 'TEXT_PREVIEW_EXT': (TEXT_PREVIEW_EXT, ''), diff --git a/seahub/templates/base_for_react.html b/seahub/templates/base_for_react.html index 7f6587364f..e9c42095a3 100644 --- a/seahub/templates/base_for_react.html +++ b/seahub/templates/base_for_react.html @@ -92,7 +92,9 @@ return libraryTemplates; })(), enableRepoSnapshotLabel: {% if enable_repo_snapshot_label %} true {% else %} false {% endif %}, + shareLinkForceUsePassword: {% if share_link_force_use_password %} true {% else %} false {% endif %}, shareLinkPasswordMinLength: {{ share_link_password_min_length }}, + shareLinkPasswordStrengthLevel: {{ share_link_password_strength_level }}, sideNavFooterCustomHtml: "{{ side_nav_footer_custom_html|safe|escapejs }}", aboutDialogCustomHtml: "{{ about_dialog_custom_html|safe|escapejs }}", maxFileName: "{{ max_file_name }}", diff --git a/seahub/templates/file_view_react.html b/seahub/templates/file_view_react.html index e32d9163a6..7e2eab1329 100644 --- a/seahub/templates/file_view_react.html +++ b/seahub/templates/file_view_react.html @@ -13,7 +13,9 @@ window.app.pageOptions = { canGenerateShareLink: {% if user.permissions.can_generate_share_link %} true {% else %} false {% endif %}, canSendShareLinkEmail: {% if user.permissions.can_send_share_link_mail %} true {% else %} false {% endif %}, + shareLinkForceUsePassword: {% if share_link_force_use_password %} true {% else %} false {% endif %}, shareLinkPasswordMinLength: {{ share_link_password_min_length }}, + shareLinkPasswordStrengthLevel: {{ share_link_password_strength_level }}, shareLinkExpireDaysDefault: {{ share_link_expire_days_default }}, shareLinkExpireDaysMin: {{ share_link_expire_days_min }}, shareLinkExpireDaysMax: {{ share_link_expire_days_max }}, diff --git a/seahub/templates/markdown_file_view_react.html b/seahub/templates/markdown_file_view_react.html index e587a68c07..ef4b969c94 100644 --- a/seahub/templates/markdown_file_view_react.html +++ b/seahub/templates/markdown_file_view_react.html @@ -47,7 +47,9 @@ hasDraft: '{{ has_draft }}' === 'True', draftFilePath: '{{ draft_file_path }}', draftOriginFilePath: '{{ draft_origin_file_path }}', + shareLinkForceUsePassword: {% if share_link_force_use_password %} true {% else %} false {% endif %}, shareLinkPasswordMinLength: {{ share_link_password_min_length }}, + shareLinkPasswordStrengthLevel: {{ share_link_password_strength_level }}, shareLinkExpireDaysDefault: {{ share_link_expire_days_default }}, shareLinkExpireDaysMin: {{ share_link_expire_days_min }}, shareLinkExpireDaysMax: {{ share_link_expire_days_max }}, diff --git a/seahub/utils/__init__.py b/seahub/utils/__init__.py index 4bb58705fe..c0af8d917b 100644 --- a/seahub/utils/__init__.py +++ b/seahub/utils/__init__.py @@ -3,19 +3,18 @@ from functools import partial import os import re -import urllib.request, urllib.parse, urllib.error -import urllib.request, urllib.error, urllib.parse +import urllib.request +import urllib.parse +import urllib.error import uuid import logging import hashlib import tempfile -import locale import configparser import mimetypes import contextlib from datetime import datetime from urllib.parse import urlparse, urljoin -import json from constance import config import seaserv @@ -24,9 +23,9 @@ from seaserv import seafile_api from django.urls import reverse from django.core.mail import EmailMessage from django.shortcuts import render -from django.template import Context, loader +from django.template import loader from django.utils.translation import ugettext as _ -from django.http import HttpResponseRedirect, HttpResponse, HttpResponseNotModified +from django.http import HttpResponseRedirect, HttpResponse from django.utils.http import urlquote from django.utils.html import escape from django.utils.timezone import make_naive, is_aware @@ -35,7 +34,7 @@ from django.views.static import serve as django_static_serve from seahub.auth import REDIRECT_FIELD_NAME from seahub.api2.models import Token, TokenV2 import seahub.settings -from seahub.settings import SITE_NAME, MEDIA_URL, LOGO_PATH, \ +from seahub.settings import MEDIA_URL, LOGO_PATH, \ MEDIA_ROOT, CUSTOM_LOGO_PATH try: from seahub.settings import EVENTS_CONFIG_FILE @@ -1254,6 +1253,16 @@ def is_user_password_strong(password): else: return True +def get_password_strength_level(password): + + num = 0 + for letter in password: + # get ascii dec + # bitwise OR + num |= get_char_mode(ord(letter)) + + return calculate_bitwise(num) + def get_char_mode(n): """Return different num according to the type of given letter: '1': num, diff --git a/seahub/views/file.py b/seahub/views/file.py index 425c5dc4b2..dabe5d420a 100644 --- a/seahub/views/file.py +++ b/seahub/views/file.py @@ -10,7 +10,9 @@ import json import time import uuid import stat -import urllib.request, urllib.error, urllib.parse +import urllib.request +import urllib.error +import urllib.parse import chardet import logging import posixpath @@ -45,7 +47,7 @@ from seahub.onlyoffice.utils import get_onlyoffice_dict from seahub.auth.decorators import login_required from seahub.base.decorators import repo_passwd_set_required from seahub.base.accounts import ANONYMOUS_EMAIL -from seahub.base.templatetags.seahub_tags import file_icon_filter, email2nickname +from seahub.base.templatetags.seahub_tags import file_icon_filter from seahub.share.models import FileShare, check_share_link_common from seahub.share.decorators import share_link_audit, share_link_login_required from seahub.wiki.utils import get_wiki_dirent @@ -87,6 +89,7 @@ import seahub.settings as settings from seahub.settings import FILE_ENCODING_LIST, FILE_PREVIEW_MAX_SIZE, \ FILE_ENCODING_TRY_LIST, MEDIA_URL, SEAFILE_COLLAB_SERVER, ENABLE_WATERMARK, \ SHARE_LINK_EXPIRE_DAYS_MIN, SHARE_LINK_EXPIRE_DAYS_MAX, SHARE_LINK_PASSWORD_MIN_LENGTH, \ + SHARE_LINK_FORCE_USE_PASSWORD, SHARE_LINK_PASSWORD_STRENGTH_LEVEL, \ SHARE_LINK_EXPIRE_DAYS_DEFAULT, ENABLE_SHARE_LINK_REPORT_ABUSE @@ -564,7 +567,9 @@ def view_lib_file(request, repo_id, path): 'highlight_keyword': settings.HIGHLIGHT_KEYWORD, 'enable_file_comment': settings.ENABLE_FILE_COMMENT, 'enable_watermark': ENABLE_WATERMARK, + 'share_link_force_use_password': SHARE_LINK_FORCE_USE_PASSWORD, 'share_link_password_min_length': SHARE_LINK_PASSWORD_MIN_LENGTH, + 'share_link_password_strength_level': SHARE_LINK_PASSWORD_STRENGTH_LEVEL, 'share_link_expire_days_default': SHARE_LINK_EXPIRE_DAYS_DEFAULT, 'share_link_expire_days_min': SHARE_LINK_EXPIRE_DAYS_MIN, 'share_link_expire_days_max': SHARE_LINK_EXPIRE_DAYS_MAX,