diff --git a/frontend/src/components/dialog/generate-share-link.js b/frontend/src/components/dialog/generate-share-link.js index 9d5a3e7a13..28d96fc88d 100644 --- a/frontend/src/components/dialog/generate-share-link.js +++ b/frontend/src/components/dialog/generate-share-link.js @@ -269,19 +269,19 @@ class GenerateShareLink extends React.Component { // validate password if (isShowPasswordInput) { if (password.length === 0) { - this.setState({errorInfo: 'Please enter password'}); + this.setState({errorInfo: gettext('Please enter a password.')}); return false; } if (password.length < shareLinkPasswordMinLength) { - this.setState({errorInfo: 'Password is too short'}); + this.setState({errorInfo: gettext('The password is too short.')}); return false; } if (password !== passwdnew) { - this.setState({errorInfo: 'Passwords don\'t match'}); + 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))}); + this.setState({errorInfo: gettext('The password is too weak. It should include at least {passwordStrengthLevel} of the following: number, upper letter, lower letter and other symbols.').replace('{passwordStrengthLevel}', shareLinkPasswordStrengthLevel)}); return false; } } @@ -289,7 +289,7 @@ class GenerateShareLink extends React.Component { if (isExpireChecked) { if (setExp == 'by-date') { if (!expDate) { - this.setState({errorInfo: 'Please select an expiration time'}); + this.setState({errorInfo: gettext('Please select an expiration time')}); return false; } return true; @@ -298,11 +298,11 @@ class GenerateShareLink extends React.Component { // by days let reg = /^\d+$/; if (!expireDays) { - this.setState({errorInfo: 'Please enter days'}); + this.setState({errorInfo: gettext('Please enter days')}); return false; } if (!reg.test(expireDays)) { - this.setState({errorInfo: 'Please enter a non-negative integer'}); + this.setState({errorInfo: gettext('Please enter a non-negative integer')}); return false; } @@ -371,9 +371,9 @@ class GenerateShareLink extends React.Component { return ; } - 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); + let passwordLengthTip = gettext('(at least {passwordMinLength} characters and includes {passwordStrengthLevel} of the following: number, upper letter, lower letter and other symbols)'); + passwordLengthTip = passwordLengthTip.replace('{passwordMinLength}', shareLinkPasswordMinLength) + .replace('{passwordStrengthLevel}', shareLinkPasswordStrengthLevel); const { userPerm } = this.props; const { isCustomPermission } = Utils.getUserPermission(userPerm); diff --git a/frontend/src/components/dialog/generate-upload-link.js b/frontend/src/components/dialog/generate-upload-link.js index c6cd1c4fef..ce44d629db 100644 --- a/frontend/src/components/dialog/generate-upload-link.js +++ b/frontend/src/components/dialog/generate-upload-link.js @@ -151,11 +151,11 @@ class GenerateUploadLink extends React.Component { // check password params if (showPasswordInput) { if (password.length === 0) { - this.setState({errorInfo: gettext('Please enter password')}); + this.setState({errorInfo: gettext('Please enter a password.')}); return false; } if (password.length < shareLinkPasswordMinLength) { - this.setState({errorInfo: gettext('Password is too short')}); + this.setState({errorInfo: gettext('The password is too short.')}); return false; } if (password !== passwordnew) { @@ -163,7 +163,7 @@ class GenerateUploadLink extends React.Component { 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))}); + this.setState({errorInfo: gettext('The password is too weak. It should include at least {passwordStrengthLevel} of the following: number, upper letter, lower letter and other symbols.').replace('{passwordStrengthLevel}', shareLinkPasswordStrengthLevel)}); return false; } } @@ -171,7 +171,7 @@ class GenerateUploadLink extends React.Component { if (isExpireChecked) { if (setExp == 'by-date') { if (!expDate) { - this.setState({errorInfo: 'Please select an expiration time'}); + this.setState({errorInfo: gettext('Please select an expiration time')}); return false; } return true; @@ -267,9 +267,9 @@ class GenerateUploadLink extends React.Component { const { isSendLinkShown } = this.state; - 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); + let passwordLengthTip = gettext('(at least {passwordMinLength} characters and includes {passwordStrengthLevel} of the following: number, upper letter, lower letter and other symbols)'); + passwordLengthTip = passwordLengthTip.replace('{passwordMinLength}', shareLinkPasswordMinLength) + .replace('{passwordStrengthLevel}', shareLinkPasswordStrengthLevel); if (this.state.sharedUploadInfo) { let sharedUploadInfo = this.state.sharedUploadInfo; diff --git a/frontend/src/components/dialog/update-webdav-password.js b/frontend/src/components/dialog/update-webdav-password.js index edaa3289d6..2d5fc24983 100644 --- a/frontend/src/components/dialog/update-webdav-password.js +++ b/frontend/src/components/dialog/update-webdav-password.js @@ -1,7 +1,8 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { Modal, ModalHeader, ModalBody, ModalFooter, Button, Input, InputGroup, InputGroupAddon } from 'reactstrap'; +import { Modal, ModalHeader, ModalBody, ModalFooter, Alert, Button, Input, InputGroup, InputGroupAddon } from 'reactstrap'; import { gettext } from '../../utils/constants'; +import { Utils } from '../../utils/utils'; const propTypes = { password: PropTypes.string.isRequired, @@ -9,6 +10,8 @@ const propTypes = { toggle: PropTypes.func.isRequired }; +const { webdavSecretMinLength, webdavSecretStrengthLevel } = window.app.pageOptions; + class UpdateWebdavPassword extends Component { constructor(props) { @@ -16,20 +19,36 @@ class UpdateWebdavPassword extends Component { this.state = { password: this.props.password, isPasswordVisible: false, - btnDisabled: false + btnDisabled: false, + errMsg: '' }; } submit = () => { + + if (this.state.password.length === 0) { + this.setState({errMsg: gettext('Please enter a password.')}); + return false; + } + if (this.state.password.length < webdavSecretMinLength) { + this.setState({errMsg: gettext('The password is too short.')}); + return false; + } + + if (Utils.getStrengthLevel(this.state.password) < webdavSecretStrengthLevel) { + this.setState({errMsg: gettext('The password is too weak. It should include at least {passwordStrengthLevel} of the following: number, upper letter, lower letter and other symbols.').replace('{passwordStrengthLevel}', webdavSecretStrengthLevel)}); + return false; + } + this.setState({ btnDisabled: true }); - this.props.updatePassword(this.state.password); + + this.props.updatePassword(this.state.password.trim()); } handleInputChange = (e) => { - let passwd = e.target.value.trim(); - this.setState({password: passwd}); + this.setState({password: e.target.value}); } togglePasswordVisible = () => { @@ -39,11 +58,7 @@ class UpdateWebdavPassword extends Component { } generatePassword = () => { - let randomPassword = ''; - const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let i = 0; i < 8; i++) { - randomPassword += possible.charAt(Math.floor(Math.random() * possible.length)); - } + let randomPassword = Utils.generatePassword(webdavSecretMinLength); this.setState({ password: randomPassword, isPasswordVisible: true @@ -52,17 +67,21 @@ class UpdateWebdavPassword extends Component { render() { const { toggle } = this.props; + const passwordTip = gettext('(at least {passwordMinLength} characters and includes {passwordStrengthLevel} of the following: number, upper letter, lower letter and other symbols)').replace('{passwordMinLength}', webdavSecretMinLength).replace('{passwordStrengthLevel}', webdavSecretStrengthLevel); + return ( {gettext('WebDav Password')} - + +

{passwordTip}

+ {this.state.errMsg && {gettext(this.state.errMsg)}}
diff --git a/frontend/src/utils/utils.js b/frontend/src/utils/utils.js index 277d54f378..354a07198e 100644 --- a/frontend/src/utils/utils.js +++ b/frontend/src/utils/utils.js @@ -1327,7 +1327,7 @@ export const Utils = { // 91~96:[~` // 97~122:a~z // 123~127:{~ - for (var i = 0; i <= length-4; i++) { + for (var i = 0; i < length-4; i++) { var num = Math.floor((Math.random() * (127-33)) + 33); password += String.fromCharCode(num); } diff --git a/seahub/api2/endpoints/webdav_secret.py b/seahub/api2/endpoints/webdav_secret.py index 1888966096..5d49e5189f 100644 --- a/seahub/api2/endpoints/webdav_secret.py +++ b/seahub/api2/endpoints/webdav_secret.py @@ -14,6 +14,7 @@ from seahub.api2.throttling import UserRateThrottle from seahub.api2.utils import api_error from seahub.options.models import UserOptions from seahub.utils.hasher import AESPasswordHasher +from seahub.utils import get_password_strength_level # Get an instance of a logger logger = logging.getLogger(__name__) @@ -37,6 +38,7 @@ class WebdavSecretView(APIView): }) def put(self, request, format=None): + if not settings.ENABLE_WEBDAV_SECRET: return api_error(status.HTTP_403_FORBIDDEN, 'Feature is not enabled.') @@ -45,11 +47,20 @@ class WebdavSecretView(APIView): username = request.user.username secret = request.data.get("secret", None) - if len(secret) >= 30: - return api_error(status.HTTP_400_BAD_REQUEST, - _("Length of WebDav password should be less than 30.")) if secret: + if len(secret) >= 30: + error_msg = _('Length of WebDav password should be less than 30.') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if len(secret) < settings.WEBDAV_SECRET_MIN_LENGTH: + error_msg = _('Password is too short.') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if get_password_strength_level(secret) < settings.WEBDAV_SECRET_STRENGTH_LEVEL: + error_msg = _('Password is too weak.') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + encoded = aes.encode(secret) UserOptions.objects.set_webdav_secret(username, encoded) else: diff --git a/seahub/profile/templates/profile/set_profile_react.html b/seahub/profile/templates/profile/set_profile_react.html index 5a054dfc79..09139697c2 100644 --- a/seahub/profile/templates/profile/set_profile_react.html +++ b/seahub/profile/templates/profile/set_profile_react.html @@ -29,6 +29,8 @@ window.app.pageOptions = { enableWebdavSecret: {% if ENABLE_WEBDAV_SECRET %} true {% else %} false {% endif %}, {% if ENABLE_WEBDAV_SECRET %} webdavPasswd: '{{ webdav_passwd|escapejs }}', + webdavSecretMinLength: {{ WEBDAV_SECRET_MIN_LENGTH }}, + webdavSecretStrengthLevel: {{ WEBDAV_SECRET_STRENGTH_LEVEL }}, {% endif %} enableAddressBook: {% if ENABLE_ADDRESSBOOK_OPT_IN %} true {% else %} false {% endif %}, diff --git a/seahub/profile/views.py b/seahub/profile/views.py index f2bcc73a8a..bde1728546 100644 --- a/seahub/profile/views.py +++ b/seahub/profile/views.py @@ -26,6 +26,7 @@ from seahub.work_weixin.utils import work_weixin_oauth_check from seahub.settings import ENABLE_DELETE_ACCOUNT, ENABLE_UPDATE_USER_INFO from seahub.dingtalk.settings import ENABLE_DINGTALK + @login_required def edit_profile(request): """ @@ -122,6 +123,8 @@ def edit_profile(request): 'ENABLE_CHANGE_PASSWORD': settings.ENABLE_CHANGE_PASSWORD, 'ENABLE_GET_AUTH_TOKEN_BY_SESSION': settings.ENABLE_GET_AUTH_TOKEN_BY_SESSION, 'ENABLE_WEBDAV_SECRET': settings.ENABLE_WEBDAV_SECRET, + 'WEBDAV_SECRET_MIN_LENGTH': settings.WEBDAV_SECRET_MIN_LENGTH, + 'WEBDAV_SECRET_STRENGTH_LEVEL': settings.WEBDAV_SECRET_STRENGTH_LEVEL, 'ENABLE_DELETE_ACCOUNT': ENABLE_DELETE_ACCOUNT, 'ENABLE_UPDATE_USER_INFO': ENABLE_UPDATE_USER_INFO, 'webdav_passwd': webdav_passwd, @@ -148,10 +151,11 @@ def edit_profile(request): resp_dict['default_device'] = default_device(request.user) resp_dict['backup_tokens'] = backup_tokens - #template = 'profile/set_profile.html' + # template = 'profile/set_profile.html' template = 'profile/set_profile_react.html' return render(request, template, resp_dict) + @login_required def user_profile(request, username): if is_valid_username(username): @@ -179,6 +183,7 @@ def user_profile(request, username): 'd_profile': d_profile, }) + @login_required def get_user_profile(request, user): data = { @@ -213,6 +218,7 @@ def get_user_profile(request, user): return HttpResponse(json.dumps(data), content_type=content_type) + @login_required def delete_user_account(request): if not ENABLE_DELETE_ACCOUNT: @@ -239,6 +245,7 @@ def delete_user_account(request): return HttpResponseRedirect(settings.LOGIN_URL) + @login_required def default_repo(request): """Handle post request to create default repo for user. diff --git a/seahub/settings.py b/seahub/settings.py index e4f045b3b8..795ebcd46a 100644 --- a/seahub/settings.py +++ b/seahub/settings.py @@ -721,6 +721,9 @@ THUMBNAIL_VIDEO_FRAME_TIME = 5 # use the frame at 5 second as thumbnail OFFICE_TEMPLATE_ROOT = os.path.join(MEDIA_ROOT, 'office-template') ENABLE_WEBDAV_SECRET = False +WEBDAV_SECRET_MIN_LENGTH = 1 +WEBDAV_SECRET_STRENGTH_LEVEL = 1 + ENABLE_USER_SET_CONTACT_EMAIL = False #####################