1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-19 18:29:23 +00:00

[passwd strength] add STRONG password check(default not enabled) when user sign-up or change-password

This commit is contained in:
imwhatiam
2014-07-29 16:42:08 +08:00
parent c8cf757191
commit 90ba936514
11 changed files with 330 additions and 215 deletions

View File

@@ -7,10 +7,13 @@ from seahub.base.accounts import User
from seahub.auth import authenticate from seahub.auth import authenticate
from seahub.auth.tokens import default_token_generator from seahub.auth.tokens import default_token_generator
from seahub.utils import IS_EMAIL_CONFIGURED, send_html_email, \ from seahub.utils import IS_EMAIL_CONFIGURED, send_html_email, \
is_valid_username, is_ldap_user is_valid_username, is_ldap_user, is_user_password_strong
from captcha.fields import CaptchaField from captcha.fields import CaptchaField
from seahub.settings import USER_STRONG_PASSWORD_REQUIRED, \
USER_PASSWORD_STRENGTH_LEVEL, USER_PASSWORD_MIN_LENGTH
class AuthenticationForm(forms.Form): class AuthenticationForm(forms.Form):
""" """
Base class for authenticating users. Extend this to get a form that accepts Base class for authenticating users. Extend this to get a form that accepts
@@ -64,7 +67,7 @@ class AuthenticationForm(forms.Form):
class CaptchaAuthenticationForm(AuthenticationForm): class CaptchaAuthenticationForm(AuthenticationForm):
captcha = CaptchaField() captcha = CaptchaField()
class PasswordResetForm(forms.Form): class PasswordResetForm(forms.Form):
email = forms.EmailField(label=_("E-mail"), max_length=255) email = forms.EmailField(label=_("E-mail"), max_length=255)
@@ -74,7 +77,7 @@ class PasswordResetForm(forms.Form):
""" """
if not IS_EMAIL_CONFIGURED: if not IS_EMAIL_CONFIGURED:
raise forms.ValidationError(_(u'Failed to send email, email service is not properly configured, please contact administrator.')) raise forms.ValidationError(_(u'Failed to send email, email service is not properly configured, please contact administrator.'))
email = self.cleaned_data["email"].lower().strip() email = self.cleaned_data["email"].lower().strip()
# TODO: add filter method to UserManager # TODO: add filter method to UserManager
@@ -121,9 +124,21 @@ class SetPasswordForm(forms.Form):
def __init__(self, user, *args, **kwargs): def __init__(self, user, *args, **kwargs):
self.user = user self.user = user
super(SetPasswordForm, self).__init__(*args, **kwargs) super(SetPasswordForm, self).__init__(*args, **kwargs)
def clean_new_password1(self):
if 'new_password1' in self.cleaned_data:
pwd = self.cleaned_data['new_password1']
if USER_STRONG_PASSWORD_REQUIRED is True:
if is_user_password_strong(pwd) is True:
return pwd
else:
raise forms.ValidationError(_("%s characters or more, include %s types or more of these: letters(case sensitive), numbers, and symbols") % (USER_PASSWORD_MIN_LENGTH, USER_PASSWORD_STRENGTH_LEVEL))
else:
return pwd
def clean_new_password2(self): def clean_new_password2(self):
password1 = self.cleaned_data.get('new_password1') password1 = self.cleaned_data.get('new_password1')
password2 = self.cleaned_data.get('new_password2') password2 = self.cleaned_data.get('new_password2')

View File

@@ -25,6 +25,8 @@ from seahub.auth.tokens import default_token_generator
from seahub.base.accounts import User from seahub.base.accounts import User
from seahub.utils import is_ldap_user from seahub.utils import is_ldap_user
from seahub.utils.ip import get_remote_ip from seahub.utils.ip import get_remote_ip
from seahub.settings import USER_PASSWORD_MIN_LENGTH, \
USER_STRONG_PASSWORD_REQUIRED, USER_PASSWORD_STRENGTH_LEVEL
# Get an instance of a logger # Get an instance of a logger
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -35,9 +37,9 @@ def log_user_in(request, user, redirect_to):
# Light security check -- make sure redirect_to isn't garbage. # Light security check -- make sure redirect_to isn't garbage.
if not redirect_to or ' ' in redirect_to: if not redirect_to or ' ' in redirect_to:
redirect_to = settings.LOGIN_REDIRECT_URL redirect_to = settings.LOGIN_REDIRECT_URL
# Heavier security check -- redirects to http://example.com should # Heavier security check -- redirects to http://example.com should
# not be allowed, but things like /view/?param=http://example.com # not be allowed, but things like /view/?param=http://example.com
# should be allowed. This regex checks if there is a '//' *before* a # should be allowed. This regex checks if there is a '//' *before* a
# question mark. # question mark.
elif '//' in redirect_to and re.match(r'[^\?]*//', redirect_to): elif '//' in redirect_to and re.match(r'[^\?]*//', redirect_to):
@@ -103,7 +105,7 @@ def _incr_login_faied_attempts(username=None, ip=None):
def _clear_login_failed_attempts(request): def _clear_login_failed_attempts(request):
"""Clear login failed attempts records. """Clear login failed attempts records.
Arguments: Arguments:
- `request`: - `request`:
""" """
@@ -167,9 +169,9 @@ def login(request, template_name='registration/login.html',
form = CaptchaAuthenticationForm(request) form = CaptchaAuthenticationForm(request)
else: else:
form = authentication_form(request) form = authentication_form(request)
request.session.set_test_cookie() request.session.set_test_cookie()
if Site._meta.installed: if Site._meta.installed:
current_site = Site.objects.get_current() current_site = Site.objects.get_current()
else: else:
@@ -213,7 +215,7 @@ def login_simple_check(request):
user = User.objects.get(email=username) user = User.objects.get(email=username)
except User.DoesNotExist: except User.DoesNotExist:
raise Http404 raise Http404
for backend in get_backends(): for backend in get_backends():
user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__) user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
@@ -223,7 +225,7 @@ def login_simple_check(request):
else: else:
raise Http404 raise Http404
def logout(request, next_page=None, template_name='registration/logged_out.html', redirect_field_name=REDIRECT_FIELD_NAME): def logout(request, next_page=None, template_name='registration/logged_out.html', redirect_field_name=REDIRECT_FIELD_NAME):
"Logs out the user and displays 'You are logged out' message." "Logs out the user and displays 'You are logged out' message."
from seahub.auth import logout from seahub.auth import logout
@@ -255,7 +257,7 @@ def redirect_to_login(next, login_url=None, redirect_field_name=REDIRECT_FIELD_N
# 4 views for password reset: # 4 views for password reset:
# - password_reset sends the mail # - password_reset sends the mail
# - password_reset_done shows a success message for the above # - password_reset_done shows a success message for the above
# - password_reset_confirm checks the link the user clicked and # - password_reset_confirm checks the link the user clicked and
# prompts for a new password # prompts for a new password
# - password_reset_complete shows a success message for the above # - password_reset_complete shows a success message for the above
@@ -352,8 +354,12 @@ def password_change(request, template_name='registration/password_change_form.ht
return HttpResponseRedirect(post_change_redirect) return HttpResponseRedirect(post_change_redirect)
else: else:
form = password_change_form(user=request.user) form = password_change_form(user=request.user)
return render_to_response(template_name, { return render_to_response(template_name, {
'form': form, 'form': form,
'min_len': USER_PASSWORD_MIN_LENGTH,
'strong_pwd_required': USER_STRONG_PASSWORD_REQUIRED,
'level': USER_PASSWORD_STRENGTH_LEVEL,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
def password_change_done(request, template_name='registration/password_change_done.html'): def password_change_done(request, template_name='registration/password_change_done.html'):

View File

@@ -13,12 +13,14 @@ from registration import signals
from seaserv import ccnet_threaded_rpc, unset_repo_passwd, is_passwd_set from seaserv import ccnet_threaded_rpc, unset_repo_passwd, is_passwd_set
from seahub.profile.models import Profile, DetailedProfile from seahub.profile.models import Profile, DetailedProfile
from seahub.utils import is_valid_username from seahub.utils import is_valid_username, is_user_password_strong
try: try:
from seahub.settings import CLOUD_MODE from seahub.settings import CLOUD_MODE
except ImportError: except ImportError:
CLOUD_MODE = False CLOUD_MODE = False
from seahub.settings import USER_STRONG_PASSWORD_REQUIRED, \
USER_PASSWORD_MIN_LENGTH, USER_PASSWORD_STRENGTH_LEVEL
UNUSABLE_PASSWORD = '!' # This will never be a valid hash UNUSABLE_PASSWORD = '!' # This will never be a valid hash
@@ -463,40 +465,14 @@ class RegistrationForm(forms.Form):
def clean_password1(self): def clean_password1(self):
if 'password1' in self.cleaned_data: if 'password1' in self.cleaned_data:
pwd = self.cleaned_data['password1'] pwd = self.cleaned_data['password1']
if len(pwd) < 6:
raise forms.ValidationError( if USER_STRONG_PASSWORD_REQUIRED is True:
_("Passwords must have at least 6 characters.")) if is_user_password_strong(pwd) is True:
return pwd
else:
raise forms.ValidationError(_("%s characters or more, include %s types or more of these: letters(case sensitive), numbers, and symbols") % (USER_PASSWORD_MIN_LENGTH, USER_PASSWORD_STRENGTH_LEVEL))
else: else:
num = 0 return pwd
for letter in pwd:
# get ascii dec
# bitwise OR
num |= self.get_char_mode(ord(letter))
level = self.caculate_bitwise(num)
if level == 1:
raise forms.ValidationError(_("Passwords must contain at least 2 types: uppercase letters, lowercase letters, numbers, and symbols"))
return self.cleaned_data['password1']
def get_char_mode(self, n):
if (n >= 48 and n <= 57): #nums
return 1;
if (n >= 65 and n <= 90): #uppers
return 2;
if (n >= 97 and n <= 122): #lowers
return 4;
else:
return 8;
def caculate_bitwise(self, num):
level = 0
for i in range(4):
# bitwise AND
if (num&1):
level += 1
# Right logical shift
num = num >> 1
return level
def clean_password2(self): def clean_password2(self):
""" """

View File

@@ -104,10 +104,10 @@ MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware', 'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'seahub.auth.middleware.AuthenticationMiddleware', 'seahub.auth.middleware.AuthenticationMiddleware',
'seahub.base.middleware.BaseMiddleware', 'seahub.base.middleware.BaseMiddleware',
'seahub.base.middleware.InfobarMiddleware', 'seahub.base.middleware.InfobarMiddleware',
) )
@@ -161,7 +161,7 @@ LOCALE_PATHS = (
TEMPLATE_CONTEXT_PROCESSORS = ( TEMPLATE_CONTEXT_PROCESSORS = (
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.core.context_processors.debug', 'django.core.context_processors.debug',
'django.core.context_processors.i18n', 'django.core.context_processors.i18n',
'django.core.context_processors.media', 'django.core.context_processors.media',
'djblets.util.context_processors.siteRoot', 'djblets.util.context_processors.siteRoot',
'django.core.context_processors.request', 'django.core.context_processors.request',
@@ -207,6 +207,18 @@ SHOW_REPO_DOWNLOAD_BUTTON = False
# mininum length for password of encrypted library # mininum length for password of encrypted library
REPO_PASSWORD_MIN_LENGTH = 8 REPO_PASSWORD_MIN_LENGTH = 8
# mininum length for user's password
USER_PASSWORD_MIN_LENGTH = 6
# LEVEL 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.
USER_PASSWORD_STRENGTH_LEVEL = 3
# default False, only check USER_PASSWORD_MIN_LENGTH
# when True, check password strength level, STRONG(or above) is allowed
USER_STRONG_PASSWORD_REQUIRED = False
# Using server side crypto by default, otherwise, let user choose crypto method. # Using server side crypto by default, otherwise, let user choose crypto method.
FORCE_SERVER_CRYPTO = True FORCE_SERVER_CRYPTO = True
@@ -219,7 +231,7 @@ OFFICE_PREVIEW_MAX_SIZE = 2 * 1024 * 1024
USE_PDFJS = True USE_PDFJS = True
FILE_ENCODING_LIST = ['auto', 'utf-8', 'gbk', 'ISO-8859-1', 'ISO-8859-5'] FILE_ENCODING_LIST = ['auto', 'utf-8', 'gbk', 'ISO-8859-1', 'ISO-8859-5']
FILE_ENCODING_TRY_LIST = ['utf-8', 'gbk'] FILE_ENCODING_TRY_LIST = ['utf-8', 'gbk']
HIGHLIGHT_KEYWORD = False # If True, highlight the keywords in the file when the visit is via clicking a link in 'search result' page. HIGHLIGHT_KEYWORD = False # If True, highlight the keywords in the file when the visit is via clicking a link in 'search result' page.
# Common settings(file extension, storage) for avatar and group avatar. # Common settings(file extension, storage) for avatar and group avatar.
AVATAR_FILE_STORAGE = '' # Replace with 'seahub.base.database_storage.DatabaseStorage' if save avatar files to database AVATAR_FILE_STORAGE = '' # Replace with 'seahub.base.database_storage.DatabaseStorage' if save avatar files to database
@@ -346,7 +358,7 @@ LOGGING = {
'filename': os.path.join(LOG_DIR, 'seahub.log'), 'filename': os.path.join(LOG_DIR, 'seahub.log'),
'maxBytes': 1024*1024*10, # 10 MB 'maxBytes': 1024*1024*10, # 10 MB
'formatter':'standard', 'formatter':'standard',
}, },
'request_handler': { 'request_handler': {
'level':'WARN', 'level':'WARN',
'class':'logging.handlers.RotatingFileHandler', 'class':'logging.handlers.RotatingFileHandler',
@@ -452,7 +464,7 @@ def load_local_settings(module):
elif re.search('^[A-Z]', attr): elif re.search('^[A-Z]', attr):
globals()[attr] = getattr(module, attr) globals()[attr] = getattr(module, attr)
# Load seahub_extra_settings.py # Load seahub_extra_settings.py
try: try:
from seahub_extra import seahub_extra_settings from seahub_extra import seahub_extra_settings
@@ -478,7 +490,7 @@ try:
except ImportError: except ImportError:
pass pass
else: else:
# In server release, sqlite3 db file is <topdir>/seahub.db # In server release, sqlite3 db file is <topdir>/seahub.db
DATABASES['default']['NAME'] = os.path.join(install_topdir, 'seahub.db') DATABASES['default']['NAME'] = os.path.join(install_topdir, 'seahub.db')
if 'win32' not in sys.platform: if 'win32' not in sys.platform:
# In server release, gunicorn is used to deploy seahub # In server release, gunicorn is used to deploy seahub

View File

@@ -7,9 +7,13 @@
<h2 class="hd">{% trans "Password Modification" %}</h2> <h2 class="hd">{% trans "Password Modification" %}</h2>
<form action="" method="post" class="con">{% csrf_token %} <form action="" method="post" class="con">{% csrf_token %}
<label for="id_old_password">{% trans "Current Password: " %}</label> <label for="id_old_password">{% trans "Current Password: " %}</label>
{{ form.old_password }} {{ form.old_password.errors }} {{ form.old_password }} {{ form.old_password.errors }}
<label for="id_new_password1">{% trans "New Password: " %}</label> <label for="id_new_password1">{% trans "New Password: " %}</label>
{{ form.new_password1 }} {{ form.new_password1.errors }} {% if strong_pwd_required %}
<span class="icon-question-sign" title="{% blocktrans %}{{min_len}} characters or more, include {{level}} types or more of these: letters(case sensitive), numbers, and symbols"{% endblocktrans%}></span>
{% endif %}
{{ form.new_password1 }} {{ form.new_password1.errors }}
<div id="pwd_strength"></div>
<label for="id_new_password2">{% trans "Confirm Password: " %}</label> <label for="id_new_password2">{% trans "Confirm Password: " %}</label>
{{ form.new_password2 }} {{ form.new_password2.errors }} {{ form.new_password2 }} {{ form.new_password2.errors }}
@@ -22,6 +26,47 @@
{% block extra_script %} {% block extra_script %}
<script type="text/javascript"> <script type="text/javascript">
$('[type="password"]').addClass('input'); $('[type="password"]').addClass('input');
{% include "snippets/password_strength_js.html" %}
$("#id_new_password1").keyup(function() {
var pwd = $(this).val(),
level = getStrengthLevel(pwd);
if (pwd.length > 0) {
showStrength(level);
};
});
$('form').submit(function(){
var old_pwd = $.trim($('input[name="old_password"]').val()),
pwd1 = $.trim($('input[name="new_password1"]').val()),
pwd2 = $.trim($('input[name="new_password2"]').val()),
level = getStrengthLevel(pwd1);
if (!old_pwd) {
$('.error').removeClass('hide').html("{% trans "Current password cannot be blank" %}");
return false;
}
if (!pwd1) {
$('.error').removeClass('hide').html("{% trans "Password cannot be blank" %}");
return false;
}
if (!pwd2) {
$('.error').removeClass('hide').html("{% trans "Please enter the password again" %}");
return false;
}
if (pwd1 != pwd2) {
$('.error').removeClass('hide').html("{% trans "Passwords don't match" %}");
return false;
}
{% if strong_pwd_required %}
if (level < {{level}}) {
$('.error').removeClass('hide').html("{% blocktrans %}{{min_len}} characters or more, include {{level}} types or more of these: letters(case sensitive), numbers, and symbols{% endblocktrans %}");
return false;
}
{% endif %}
});
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -16,7 +16,10 @@
<label for="id_email">{% trans "Email" %}</label> <label for="id_email">{% trans "Email" %}</label>
{{ form.email }} {{ form.email.errors }} {{ form.email }} {{ form.email.errors }}
<label for="id_password1">{% trans "Password" %}</label> <span class="icon-question-sign" title="{% trans "6 characters or more, include 2 types or more of these: letters(case sensitive), numbers, and symbols" %}"></span> <label for="id_password1">{% trans "Password" %}</label>
{% if strong_pwd_required %}
<span class="icon-question-sign" title="{% blocktrans %}{{min_len}} characters or more, include {{level}} types or more of these: letters(case sensitive), numbers, and symbols"{% endblocktrans%}></span>
{% endif %}
{{ form.password1 }} {{ form.password1.errors }} {{ form.password1 }} {{ form.password1.errors }}
<div id="pwd_strength"></div> <div id="pwd_strength"></div>
<label for="id_password2">{% trans "Confirm Password" %}</label> <label for="id_password2">{% trans "Confirm Password" %}</label>
@@ -40,73 +43,17 @@
{% block extra_script %} {% block extra_script %}
<script type="text/javascript"> <script type="text/javascript">
{% include "snippets/password_strength_js.html" %}
$("#id_password1").keyup(function() { $("#id_password1").keyup(function() {
var level = getStrengthLevel($(this).val()); var pwd = $(this).val(),
showStrength(level); level = getStrengthLevel(pwd);
});
function getStrengthLevel(pwd) { if (pwd.length > 0) {
var num = 0, showStrength(level);
min_passwd_len = 6;
if (pwd.length < min_passwd_len) {
return 0;
} else {
for (var i = 0; i < pwd.length; i++) {
// return the unicode
// bitwise OR
num |= getCharMode(pwd.charCodeAt(i));
};
return calculateBitwise(num);
}; };
} });
function getCharMode(n) {
if (n >= 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;
}
function calculateBitwise(num) {
var level = 0;
for (var i = 0; i < 4; i++){
// bitwise AND
if (num&1) level++;
// Right logical shift
num>>>=1;
}
return level;
}
function showStrength(level) {
var strength_ct = $("#pwd_strength");
var strength = [
"{% trans "too weak" %}",
"{% trans "weak" %}",
"{% trans "medium" %}",
"{% trans "strong" %}"
];
switch (level) {
case 0:
strength_ct.html(strength[0]).css("color", '#F00000');
break;
case 1:
strength_ct.html('<span style="background:#db4747;">' + strength[1] + '</span><span>' + strength[2] + '</span><span>' + strength[3] + '</span>');
break;
case 2:
strength_ct.html('<span>' + strength[1] + '</span><span style="background:#fdd64d;">' + strength[2] + '</span><span>' + strength[3] + '</span>');
break;
case 3:
case 4:
strength_ct.html('<span>' + strength[1] + '</span><span>' + strength[2] + '</span><span style="background:#4aa323;">' + strength[3] + '</span>');
break;
}
}
$('form').submit(function(){ $('form').submit(function(){
var email = $.trim($('input[name="email"]').val()), var email = $.trim($('input[name="email"]').val()),
@@ -130,14 +77,12 @@ $('form').submit(function(){
$('.error').removeClass('hide').html("{% trans "Passwords don't match" %}"); $('.error').removeClass('hide').html("{% trans "Passwords don't match" %}");
return false; return false;
} }
if (pwd1.length < 6) { {% if strong_pwd_required %}
$('.error').removeClass('hide').html("{% trans "Passwords must have at least 6 characters" %}"); if (level < {{level}}) {
return false; $('.error').removeClass('hide').html("{% blocktrans %}{{min_len}} characters or more, include {{level}} types or more of these: letters(case sensitive), numbers, and symbols{% endblocktrans %}");
} return false;
if (level <= 2) { }
$('.error').removeClass('hide').html("{% trans "Passwords contain at least 2 types: uppercase letters, lowercase letters, numbers, and symbols" %}"); {% endif %}
return false;
}
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -0,0 +1,62 @@
{% load i18n %}
function getStrengthLevel(pwd) {
var num = 0;
if (pwd.length < {{min_len}}) {
return 0;
} else {
for (var i = 0; i < pwd.length; i++) {
// return the unicode
// bitwise OR
num |= getCharMode(pwd.charCodeAt(i));
};
return calculateBitwise(num);
};
}
function getCharMode(n) {
if (n >= 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;
}
function calculateBitwise(num) {
var level = 0;
for (var i = 0; i < 4; i++){
// bitwise AND
if (num&1) level++;
// Right logical shift
num>>>=1;
}
return level;
}
function showStrength(level) {
var strength_ct = $("#pwd_strength");
var strength = [
"{% trans "too weak" %}",
"{% trans "weak" %}",
"{% trans "medium" %}",
"{% trans "strong" %}"
];
switch (level) {
case 0:
strength_ct.html(strength[0]).css("color", '#F00000');
break;
case 1:
strength_ct.html('<span style="background:#db4747;">' + strength[1] + '</span><span>' + strength[2] + '</span><span>' + strength[3] + '</span>');
break;
case 2:
strength_ct.html('<span>' + strength[1] + '</span><span style="background:#fdd64d;">' + strength[2] + '</span><span>' + strength[3] + '</span>');
break;
case 3:
case 4:
strength_ct.html('<span>' + strength[1] + '</span><span>' + strength[2] + '</span><span style="background:#4aa323;">' + strength[3] + '</span>');
break;
}
}

View File

@@ -15,7 +15,7 @@ $(function() {
$('#encrypt-switch').click(function () { $('#encrypt-switch').click(function () {
var form = $('#repo-create-form'), var form = $('#repo-create-form'),
pwd_input = $('input[type="password"]', form); pwd_input = $('input[type="password"]', form);
if ($(this).attr('checked')) { if ($(this).attr('checked')) {
pwd_input.attr('disabled', false).removeClass('input-disabled'); pwd_input.attr('disabled', false).removeClass('input-disabled');
} else { } else {
@@ -23,7 +23,7 @@ $('#encrypt-switch').click(function () {
} }
}); });
$('#repo-create-form').submit(function() { $('#repo-create-form').submit(function() {
var form = $(this), var form = $(this),
form_id = form.attr('id'), form_id = form.attr('id'),
name = $('[name="repo_name"]', form).val(), name = $('[name="repo_name"]', form).val(),
desc = $('[name="repo_desc"]', form).val(), desc = $('[name="repo_desc"]', form).val(),

View File

@@ -29,7 +29,8 @@ from seaserv import seafserv_rpc, seafserv_threaded_rpc, get_repo, get_commits,\
list_personal_repos_by_owner, get_group_repos, \ list_personal_repos_by_owner, get_group_repos, \
list_inner_pub_repos, CCNET_CONF_PATH, SERVICE_URL list_inner_pub_repos, CCNET_CONF_PATH, SERVICE_URL
import seahub.settings import seahub.settings
from seahub.settings import SITE_NAME, MEDIA_URL, LOGO_PATH from seahub.settings import SITE_NAME, MEDIA_URL, LOGO_PATH, \
USER_PASSWORD_STRENGTH_LEVEL, USER_PASSWORD_MIN_LENGTH
try: try:
from seahub.settings import EVENTS_CONFIG_FILE from seahub.settings import EVENTS_CONFIG_FILE
except ImportError: except ImportError:
@@ -78,7 +79,7 @@ PREVIEW_FILEEXT = {
def gen_fileext_type_map(): def gen_fileext_type_map():
""" """
Generate previewed file extension and file type relation map. Generate previewed file extension and file type relation map.
""" """
d = {} d = {}
for filetype in PREVIEW_FILEEXT.keys(): for filetype in PREVIEW_FILEEXT.keys():
@@ -156,7 +157,7 @@ def gen_token(max_length=5):
Generate a random token. Generate a random token.
""" """
return uuid.uuid4().hex[:max_length] return uuid.uuid4().hex[:max_length]
def normalize_cache_key(value, prefix=None): def normalize_cache_key(value, prefix=None):
@@ -165,7 +166,7 @@ def normalize_cache_key(value, prefix=None):
""" """
key = value if prefix is None else prefix + value key = value if prefix is None else prefix + value
return urlquote(key) return urlquote(key)
def get_repo_last_modify(repo): def get_repo_last_modify(repo):
""" Get last modification time for a repo. """ Get last modification time for a repo.
@@ -332,7 +333,7 @@ def get_file_revision_id_size(repo_id, commit_id, path):
def new_merge_with_no_conflict(commit): def new_merge_with_no_conflict(commit):
"""Check whether a commit is a new merge, and no conflict. """Check whether a commit is a new merge, and no conflict.
Arguments: Arguments:
- `commit`: - `commit`:
""" """
@@ -358,7 +359,7 @@ def get_commit_before_new_merge(commit):
commit = p1 if p1.ctime > p2.ctime else p2 commit = p1 if p1.ctime > p2.ctime else p2
assert new_merge_with_no_conflict(commit) is False assert new_merge_with_no_conflict(commit) is False
return commit return commit
def gen_inner_file_get_url(token, filename): def gen_inner_file_get_url(token, filename):
@@ -386,7 +387,7 @@ def get_max_upload_file_size():
Returns ``None`` if this value is not set. Returns ``None`` if this value is not set.
""" """
return seaserv.MAX_UPLOAD_FILE_SIZE return seaserv.MAX_UPLOAD_FILE_SIZE
def gen_block_get_url(token, blkid): def gen_block_get_url(token, blkid):
""" """
Generate fileserver block url. Generate fileserver block url.
@@ -425,7 +426,7 @@ def string2list(string):
continue continue
s.add(e) s.add(e)
return [ x for x in s ] return [ x for x in s ]
# def get_cur_ctx(request): # def get_cur_ctx(request):
# ctx_dict = request.session.get('current_context', { # ctx_dict = request.session.get('current_context', {
# 'base_template': 'myhome_base.html', # 'base_template': 'myhome_base.html',
@@ -439,12 +440,12 @@ def string2list(string):
def is_org_context(request): def is_org_context(request):
"""An organization context is a virtual private Seafile instance on cloud """An organization context is a virtual private Seafile instance on cloud
service. service.
Arguments: Arguments:
- `request`: - `request`:
""" """
return request.cloud_mode and request.user.org is not None return request.cloud_mode and request.user.org is not None
# def check_and_get_org_by_repo(repo_id, user): # def check_and_get_org_by_repo(repo_id, user):
# """ # """
# Check whether repo is org repo, get org info if it is, and set # Check whether repo is org repo, get org info if it is, and set
@@ -460,7 +461,7 @@ def is_org_context(request):
# else: # else:
# org = None # org = None
# base_template = 'myhome_base.html' # base_template = 'myhome_base.html'
# return org, base_template # return org, base_template
def check_and_get_org_by_group(group_id, user): def check_and_get_org_by_group(group_id, user):
@@ -478,9 +479,9 @@ def check_and_get_org_by_group(group_id, user):
else: else:
org = None org = None
base_template = 'myhome_base.html' base_template = 'myhome_base.html'
return org, base_template return org, base_template
# events related # events related
if EVENTS_CONFIG_FILE: if EVENTS_CONFIG_FILE:
import seafevents import seafevents
@@ -510,7 +511,7 @@ if EVENTS_CONFIG_FILE:
events = _get_events_inner(ev_session, username, next_start, count) events = _get_events_inner(ev_session, username, next_start, count)
if not events: if not events:
break break
for e1 in events: for e1 in events:
duplicate = False duplicate = False
for e2 in valid_events: for e2 in valid_events:
@@ -519,13 +520,13 @@ if EVENTS_CONFIG_FILE:
new_merge = False new_merge = False
if hasattr(e1, 'commit') and new_merge_with_no_conflict(e1.commit): if hasattr(e1, 'commit') and new_merge_with_no_conflict(e1.commit):
new_merge = True new_merge = True
if not duplicate and not new_merge: if not duplicate and not new_merge:
valid_events.append(e1) valid_events.append(e1)
total_used = total_used + 1 total_used = total_used + 1
if len(valid_events) == count: if len(valid_events) == count:
break break
if len(valid_events) == count: if len(valid_events) == count:
break break
next_start = next_start + len(events) next_start = next_start + len(events)
@@ -569,14 +570,14 @@ if EVENTS_CONFIG_FILE:
break break
if len(valid_events) == limit: if len(valid_events) == limit:
break break
next_start = next_start + len(valid_events) next_start = next_start + len(valid_events)
return valid_events return valid_events
def get_user_events(username, start, count): def get_user_events(username, start, count):
"""Return user events list and a new start. """Return user events list and a new start.
For example: For example:
``get_user_events('foo@example.com', 0, 10)`` returns the first 10 ``get_user_events('foo@example.com', 0, 10)`` returns the first 10
events. events.
@@ -584,7 +585,7 @@ if EVENTS_CONFIG_FILE:
15th events. 15th events.
""" """
return _get_events(username, start, count) return _get_events(username, start, count)
def get_org_user_events(org_id, username, start, count): def get_org_user_events(org_id, username, start, count):
return _get_events(username, start, count, org_id=org_id) return _get_events(username, start, count, org_id=org_id)
@@ -600,7 +601,7 @@ def calc_file_path_hash(path, bits=12):
path = path.encode('UTF-8') path = path.encode('UTF-8')
path_hash = hashlib.md5(urllib2.quote(path)).hexdigest()[:bits] path_hash = hashlib.md5(urllib2.quote(path)).hexdigest()[:bits]
return path_hash return path_hash
def get_service_url(): def get_service_url():
@@ -616,7 +617,7 @@ def get_server_id():
def get_site_scheme_and_netloc(): def get_site_scheme_and_netloc():
"""Return a string contains site scheme and network location part from """Return a string contains site scheme and network location part from
service url. service url.
For example: For example:
>>> get_site_scheme_and_netloc("https://example.com:8000/seafile/") >>> get_site_scheme_and_netloc("https://example.com:8000/seafile/")
https://example.com:8000 https://example.com:8000
@@ -629,7 +630,7 @@ def send_html_email(subject, con_template, con_context, from_email, to_email):
"""Send HTML email """Send HTML email
""" """
base_context = { base_context = {
'url_base': get_site_scheme_and_netloc(), 'url_base': get_site_scheme_and_netloc(),
'site_name': SITE_NAME, 'site_name': SITE_NAME,
'media_url': MEDIA_URL, 'media_url': MEDIA_URL,
'logo_path': LOGO_PATH, 'logo_path': LOGO_PATH,
@@ -639,7 +640,7 @@ def send_html_email(subject, con_template, con_context, from_email, to_email):
msg = EmailMessage(subject, t.render(Context(con_context)), from_email, to_email) msg = EmailMessage(subject, t.render(Context(con_context)), from_email, to_email)
msg.content_subtype = "html" msg.content_subtype = "html"
msg.send() msg.send()
def gen_dir_share_link(token): def gen_dir_share_link(token):
"""Generate directory share link. """Generate directory share link.
""" """
@@ -727,7 +728,7 @@ def convert_cmmt_desc_link(commit):
""" """
repo_id = commit.repo_id repo_id = commit.repo_id
cmmt_id = commit.id cmmt_id = commit.id
conv_link_url = reverse('convert_cmmt_desc_link') conv_link_url = reverse('convert_cmmt_desc_link')
def link_repl(matchobj): def link_repl(matchobj):
op = matchobj.group(1) op = matchobj.group(1)
@@ -776,7 +777,7 @@ def api_convert_desc_link(e):
if file_or_dir not in d.name: if file_or_dir not in d.name:
# skip to next diff_result if file/folder user clicked does not # skip to next diff_result if file/folder user clicked does not
# match the diff_result # match the diff_result
continue continue
if d.status == 'add' or d.status == 'mod': if d.status == 'add' or d.status == 'mod':
e.link = "api://repo/%s/files/?p=/%s" % (repo_id, d.name) e.link = "api://repo/%s/files/?p=/%s" % (repo_id, d.name)
@@ -820,7 +821,7 @@ if EVENTS_CONFIG_FILE:
return seafevents.get_office_converter_limit(config) return seafevents.get_office_converter_limit(config)
HAS_OFFICE_CONVERTER = check_office_converter_enabled() HAS_OFFICE_CONVERTER = check_office_converter_enabled()
if HAS_OFFICE_CONVERTER: if HAS_OFFICE_CONVERTER:
OFFICE_HTML_DIR = get_office_converter_html_dir() OFFICE_HTML_DIR = get_office_converter_html_dir()
@@ -849,7 +850,7 @@ if HAS_OFFICE_CONVERTER:
rpc = _get_office_converter_rpc() rpc = _get_office_converter_rpc()
return rpc.query_file_pages(file_id) return rpc.query_file_pages(file_id)
def get_converted_html_detail(file_id): def get_converted_html_detail(file_id):
d = {} d = {}
outline_file = os.path.join(OFFICE_HTML_DIR, file_id, 'file.outline') outline_file = os.path.join(OFFICE_HTML_DIR, file_id, 'file.outline')
@@ -945,3 +946,51 @@ def user_traffic_over_limit(username):
month_traffic = stat['file_view'] + stat['file_download'] + stat['dir_download'] month_traffic = stat['file_view'] + stat['file_download'] + stat['dir_download']
return True if month_traffic >= traffic_limit else False return True if month_traffic >= traffic_limit else False
def is_user_password_strong(password):
"""Return ``True`` if user's password is STRONG, otherwise ``False``.
STRONG means password has at least USER_PASSWORD_STRENGTH_LEVEL(3) types of the bellow:
num, upper letter, lower letter, other symbols
"""
if len(password) < USER_PASSWORD_MIN_LENGTH:
return False
else:
num = 0
for letter in password:
# get ascii dec
# bitwise OR
num |= get_char_mode(ord(letter))
if calculate_bitwise(num) < USER_PASSWORD_STRENGTH_LEVEL:
return False
else:
return True
def get_char_mode(n):
"""Return different num according to the type of given letter:
'1': num,
'2': upper_letter,
'4': lower_letter,
'8': other symbols
"""
if (n >= 48 and n <= 57): #nums
return 1;
if (n >= 65 and n <= 90): #uppers
return 2;
if (n >= 97 and n <= 122): #lowers
return 4;
else:
return 8;
def calculate_bitwise(num):
"""Return different level according to the given num:
"""
level = 0
for i in range(4):
# bitwise AND
if (num&1):
level += 1
# Right logical shift
num = num >> 1
return level

View File

@@ -79,7 +79,7 @@ def root(request):
def validate_owner(request, repo_id): def validate_owner(request, repo_id):
""" """
Check whether user in the request owns the repo. Check whether user in the request owns the repo.
""" """
ret = is_repo_owner(request.user.username, repo_id) ret = is_repo_owner(request.user.username, repo_id)
@@ -131,7 +131,7 @@ def get_system_default_repo_id():
def check_repo_access_permission(repo_id, user): def check_repo_access_permission(repo_id, user):
"""Check repo access permission of a user, always return 'rw' when repo is """Check repo access permission of a user, always return 'rw' when repo is
system repo and user is admin. system repo and user is admin.
Arguments: Arguments:
- `repo_id`: - `repo_id`:
- `user`: - `user`:
@@ -140,13 +140,13 @@ def check_repo_access_permission(repo_id, user):
return 'rw' return 'rw'
else: else:
return seafile_api.check_repo_access_permission(repo_id, user.username) return seafile_api.check_repo_access_permission(repo_id, user.username)
def get_file_access_permission(repo_id, path, username): def get_file_access_permission(repo_id, path, username):
"""Check user has permission to view the file. """Check user has permission to view the file.
1. check whether this file is private shared. 1. check whether this file is private shared.
2. if failed, check whether the parent of this directory is private shared. 2. if failed, check whether the parent of this directory is private shared.
""" """
pfs = PrivateFileDirShare.objects.get_private_share_in_file(username, pfs = PrivateFileDirShare.objects.get_private_share_in_file(username,
repo_id, path) repo_id, path)
if pfs is None: if pfs is None:
@@ -157,11 +157,11 @@ def get_file_access_permission(repo_id, path, username):
return None return None
else: else:
return pfs.permission return pfs.permission
def gen_path_link(path, repo_name): def gen_path_link(path, repo_name):
""" """
Generate navigate paths and links in repo page. Generate navigate paths and links in repo page.
""" """
if path and path[-1] != '/': if path and path[-1] != '/':
path += '/' path += '/'
@@ -178,9 +178,9 @@ def gen_path_link(path, repo_name):
if repo_name: if repo_name:
paths.insert(0, repo_name) paths.insert(0, repo_name)
links.insert(0, '/') links.insert(0, '/')
zipped = zip(paths, links) zipped = zip(paths, links)
return zipped return zipped
def get_repo_dirents(request, repo, commit, path, offset=-1, limit=-1): def get_repo_dirents(request, repo, commit, path, offset=-1, limit=-1):
@@ -584,7 +584,7 @@ def repo_change_passwd(request, repo_id):
{'repo_name': repo.name}) {'repo_name': repo.name})
return HttpResponse(json.dumps({'success': True}), return HttpResponse(json.dumps({'success': True}),
content_type=content_type) content_type=content_type)
def upload_error_msg (code): def upload_error_msg (code):
err_msg = _(u'Internal Server Error') err_msg = _(u'Internal Server Error')
if (code == 0): if (code == 0):
@@ -644,7 +644,7 @@ def update_file_error(request, repo_id):
'zipped': zipped, 'zipped': zipped,
'err_msg': err_msg, 'err_msg': err_msg,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
@login_required @login_required
def repo_history(request, repo_id): def repo_history(request, repo_id):
""" """
@@ -663,8 +663,8 @@ def repo_history(request, repo_id):
server_crypto = UserOptions.objects.is_server_crypto(username) server_crypto = UserOptions.objects.is_server_crypto(username)
except CryptoOptionNotSetError: except CryptoOptionNotSetError:
# Assume server_crypto is ``False`` if this option is not set. # Assume server_crypto is ``False`` if this option is not set.
server_crypto = False server_crypto = False
password_set = False password_set = False
if repo.props.encrypted and \ if repo.props.encrypted and \
(repo.enc_version == 1 or (repo.enc_version == 2 and server_crypto)): (repo.enc_version == 1 or (repo.enc_version == 2 and server_crypto)):
@@ -723,8 +723,8 @@ def repo_view_snapshot(request, repo_id):
server_crypto = UserOptions.objects.is_server_crypto(username) server_crypto = UserOptions.objects.is_server_crypto(username)
except CryptoOptionNotSetError: except CryptoOptionNotSetError:
# Assume server_crypto is ``False`` if this option is not set. # Assume server_crypto is ``False`` if this option is not set.
server_crypto = False server_crypto = False
password_set = False password_set = False
if repo.props.encrypted and \ if repo.props.encrypted and \
(repo.enc_version == 1 or (repo.enc_version == 2 and server_crypto)): (repo.enc_version == 1 or (repo.enc_version == 2 and server_crypto)):
@@ -779,8 +779,8 @@ def repo_history_revert(request, repo_id):
server_crypto = UserOptions.objects.is_server_crypto(username) server_crypto = UserOptions.objects.is_server_crypto(username)
except CryptoOptionNotSetError: except CryptoOptionNotSetError:
# Assume server_crypto is ``False`` if this option is not set. # Assume server_crypto is ``False`` if this option is not set.
server_crypto = False server_crypto = False
password_set = False password_set = False
if repo.props.encrypted and \ if repo.props.encrypted and \
(repo.enc_version == 1 or (repo.enc_version == 2 and server_crypto)): (repo.enc_version == 1 or (repo.enc_version == 2 and server_crypto)):
@@ -889,7 +889,7 @@ def create_default_library(request):
default_repo, '/', obj_name, username, 0) default_repo, '/', obj_name, username, 0)
except SearpcError as e: except SearpcError as e:
logger.error(e) logger.error(e)
return return
UserOptions.objects.set_default_repo(username, default_repo) UserOptions.objects.set_default_repo(username, default_repo)
@@ -984,7 +984,7 @@ def myhome(request):
@user_mods_check @user_mods_check
def starred(request): def starred(request):
"""List starred files. """List starred files.
Arguments: Arguments:
- `request`: - `request`:
""" """
@@ -995,7 +995,7 @@ def starred(request):
return render_to_response('starred.html', { return render_to_response('starred.html', {
"starred_files": starred_files, "starred_files": starred_files,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
@login_required @login_required
@user_mods_check @user_mods_check
@@ -1007,24 +1007,24 @@ def devices(request):
return render_to_response('devices.html', { return render_to_response('devices.html', {
"devices": user_devices, "devices": user_devices,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
@login_required_ajax @login_required_ajax
def unlink_device(request): def unlink_device(request):
content_type = 'application/json; charset=utf-8' content_type = 'application/json; charset=utf-8'
platform = request.POST.get('platform', '') platform = request.POST.get('platform', '')
device_id = request.POST.get('device_id', '') device_id = request.POST.get('device_id', '')
if not platform or not device_id: if not platform or not device_id:
return HttpResponseBadRequest(json.dumps({'error': _(u'Argument missing')}), return HttpResponseBadRequest(json.dumps({'error': _(u'Argument missing')}),
content_type=content_type) content_type=content_type)
try: try:
do_unlink_device(request.user.username, platform, device_id) do_unlink_device(request.user.username, platform, device_id)
except: except:
return HttpResponse(json.dumps({'error': _(u'Internal server error')}), return HttpResponse(json.dumps({'error': _(u'Internal server error')}),
status=500, content_type=content_type) status=500, content_type=content_type)
return HttpResponse(json.dumps({'success': True}), content_type=content_type) return HttpResponse(json.dumps({'success': True}), content_type=content_type)
@login_required @login_required
@@ -1078,7 +1078,7 @@ def unsetinnerpub(request, repo_id):
# quota_usage = seafserv_threaded_rpc.get_user_quota_usage(owner_name) # quota_usage = seafserv_threaded_rpc.get_user_quota_usage(owner_name)
# user_dict = user_info(request, owner_name) # user_dict = user_info(request, owner_name)
# return render_to_response('ownerhome.html', { # return render_to_response('ownerhome.html', {
# "owned_repos": owned_repos, # "owned_repos": owned_repos,
# "quota_usage": quota_usage, # "quota_usage": quota_usage,
@@ -1090,10 +1090,10 @@ def unsetinnerpub(request, repo_id):
def repo_set_access_property(request, repo_id): def repo_set_access_property(request, repo_id):
ap = request.GET.get('ap', '') ap = request.GET.get('ap', '')
seafserv_threaded_rpc.repo_set_access_property(repo_id, ap) seafserv_threaded_rpc.repo_set_access_property(repo_id, ap)
return HttpResponseRedirect(reverse('repo', args=[repo_id])) return HttpResponseRedirect(reverse('repo', args=[repo_id]))
@login_required @login_required
def repo_del_file(request, repo_id): def repo_del_file(request, repo_id):
if check_repo_access_permission(repo_id, request.user) != 'rw': if check_repo_access_permission(repo_id, request.user) != 'rw':
return render_permission_error(request, _('Failed to delete file.')) return render_permission_error(request, _('Failed to delete file.'))
@@ -1109,7 +1109,7 @@ def repo_del_file(request, repo_id):
url = reverse('repo', args=[repo_id]) + ('?p=%s' % urllib2.quote(parent_dir.encode('utf-8'))) url = reverse('repo', args=[repo_id]) + ('?p=%s' % urllib2.quote(parent_dir.encode('utf-8')))
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
def repo_access_file(request, repo_id, obj_id): def repo_access_file(request, repo_id, obj_id):
"""Delete or download file. """Delete or download file.
TODO: need to be rewrite. TODO: need to be rewrite.
@@ -1190,7 +1190,7 @@ def file_upload_progress_page(request):
'upload_progress_con_id': upload_progress_con_id, 'upload_progress_con_id': upload_progress_con_id,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
@login_required @login_required
def validate_filename(request): def validate_filename(request):
repo_id = request.GET.get('repo_id') repo_id = request.GET.get('repo_id')
filename = request.GET.get('filename') filename = request.GET.get('filename')
@@ -1240,7 +1240,7 @@ def render_file_revisions (request, repo_id):
if not commits: if not commits:
return render_error(request) return render_error(request)
# Check whether user is repo owner # Check whether user is repo owner
if validate_owner(request, repo_id): if validate_owner(request, repo_id):
is_owner = True is_owner = True
@@ -1400,10 +1400,10 @@ def view_shared_dir(request, token):
repo_id = fileshare.repo_id repo_id = fileshare.repo_id
path = request.GET.get('p', '') path = request.GET.get('p', '')
path = fileshare.path if not path else path path = fileshare.path if not path else path
if path[-1] != '/': # Normalize dir path if path[-1] != '/': # Normalize dir path
path += '/' path += '/'
if not path.startswith(fileshare.path): if not path.startswith(fileshare.path):
path = fileshare.path # Can not view upper dir of shared dir path = fileshare.path # Can not view upper dir of shared dir
repo = get_repo(repo_id) repo = get_repo(repo_id)
@@ -1417,13 +1417,13 @@ def view_shared_dir(request, token):
zipped = gen_path_link(path, '') zipped = gen_path_link(path, '')
if path == fileshare.path: # When user view the shared dir.. if path == fileshare.path: # When user view the shared dir..
# increase shared link view_cnt, # increase shared link view_cnt,
fileshare = FileShare.objects.get(token=token) fileshare = FileShare.objects.get(token=token)
fileshare.view_cnt = F('view_cnt') + 1 fileshare.view_cnt = F('view_cnt') + 1
fileshare.save() fileshare.save()
traffic_over_limit = user_traffic_over_limit(fileshare.username) traffic_over_limit = user_traffic_over_limit(fileshare.username)
return render_to_response('view_shared_dir.html', { return render_to_response('view_shared_dir.html', {
'repo': repo, 'repo': repo,
'token': token, 'token': token,
@@ -1461,7 +1461,7 @@ def view_shared_upload_link(request, token):
else: else:
return render_to_response('share_access_validation.html', d, return render_to_response('share_access_validation.html', d,
context_instance=RequestContext(request)) context_instance=RequestContext(request))
username = uploadlink.username username = uploadlink.username
repo_id = uploadlink.repo_id repo_id = uploadlink.repo_id
path = uploadlink.path path = uploadlink.path
@@ -1525,9 +1525,9 @@ def pubrepo(request):
""" """
if not request.user.permissions.can_view_org(): if not request.user.permissions.can_view_org():
raise Http404 raise Http404
username = request.user.username username = request.user.username
if request.cloud_mode and request.user.org is not None: if request.cloud_mode and request.user.org is not None:
org_id = request.user.org.org_id org_id = request.user.org.org_id
public_repos = seaserv.list_org_inner_pub_repos(org_id, username) public_repos = seaserv.list_org_inner_pub_repos(org_id, username)
@@ -1538,7 +1538,7 @@ def pubrepo(request):
'public_repos': public_repos, 'public_repos': public_repos,
'create_shared_repo': True, 'create_shared_repo': True,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
if not request.cloud_mode: if not request.cloud_mode:
public_repos = seaserv.list_inner_pub_repos(username) public_repos = seaserv.list_inner_pub_repos(username)
for r in public_repos: for r in public_repos:
@@ -1558,14 +1558,14 @@ def pubgrp(request):
""" """
if not request.user.permissions.can_view_org(): if not request.user.permissions.can_view_org():
raise Http404 raise Http404
if request.cloud_mode and request.user.org is not None: if request.cloud_mode and request.user.org is not None:
org_id = request.user.org.org_id org_id = request.user.org.org_id
groups = seaserv.get_org_groups(org_id, -1, -1) groups = seaserv.get_org_groups(org_id, -1, -1)
return render_to_response('organizations/pubgrp.html', { return render_to_response('organizations/pubgrp.html', {
'groups': groups, 'groups': groups,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
if not request.cloud_mode: if not request.cloud_mode:
groups = seaserv.get_personal_groups(-1, -1) groups = seaserv.get_personal_groups(-1, -1)
return render_to_response('pubgrp.html', { return render_to_response('pubgrp.html', {
@@ -1579,7 +1579,7 @@ def get_pub_users(request, start, limit):
url_prefix = request.user.org.url_prefix url_prefix = request.user.org.url_prefix
users_plus_one = seaserv.get_org_users_by_url_prefix(url_prefix, users_plus_one = seaserv.get_org_users_by_url_prefix(url_prefix,
start, limit) start, limit)
elif request.cloud_mode: elif request.cloud_mode:
raise Http404 # no pubuser in cloud mode raise Http404 # no pubuser in cloud mode
@@ -1608,7 +1608,7 @@ def pubuser(request):
""" """
if not request.user.permissions.can_view_org(): if not request.user.permissions.can_view_org():
raise Http404 raise Http404
# Make sure page request is an int. If not, deliver first page. # Make sure page request is an int. If not, deliver first page.
try: try:
current_page = int(request.GET.get('page', '1')) current_page = int(request.GET.get('page', '1'))
@@ -1629,21 +1629,21 @@ def pubuser(request):
users = users_plus_one[:per_page] users = users_plus_one[:per_page]
username = request.user.username username = request.user.username
contacts = Contact.objects.get_contacts_by_user(username) contacts = Contact.objects.get_contacts_by_user(username)
contact_emails = [] contact_emails = []
for c in contacts: for c in contacts:
contact_emails.append(c.contact_email) contact_emails.append(c.contact_email)
for u in users: for u in users:
if u.email == username or u.email in contact_emails: if u.email == username or u.email in contact_emails:
u.can_be_contact = False u.can_be_contact = False
else: else:
u.can_be_contact = True u.can_be_contact = True
return render_to_response('pubuser.html', { return render_to_response('pubuser.html', {
'users': users, 'users': users,
'current_page': current_page, 'current_page': current_page,
'has_prev': has_prev, 'has_prev': has_prev,
'has_next': has_next, 'has_next': has_next,
'page_range': page_range, 'page_range': page_range,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
@login_required_ajax @login_required_ajax
@@ -1693,7 +1693,7 @@ def repo_download_dir(request, repo_id):
dirname = os.path.basename(path.rstrip('/')) # Here use `rstrip` to cut out last '/' in path dirname = os.path.basename(path.rstrip('/')) # Here use `rstrip` to cut out last '/' in path
else: else:
dirname = repo.name dirname = repo.name
allow_download = False allow_download = False
fileshare_token = request.GET.get('t', '') fileshare_token = request.GET.get('t', '')
from_shared_link = False from_shared_link = False
@@ -1789,18 +1789,18 @@ def group_events_data(events):
utc = dt.replace(tzinfo=timezone.utc) utc = dt.replace(tzinfo=timezone.utc)
local = timezone.make_naive(utc, tz) local = timezone.make_naive(utc, tz)
return local return local
event_groups = [] event_groups = []
for e in events: for e in events:
e.time = utc_to_local(e.timestamp) e.time = utc_to_local(e.timestamp)
e.date = e.time.strftime("%Y-%m-%d") e.date = e.time.strftime("%Y-%m-%d")
if e.etype == 'repo-update': if e.etype == 'repo-update':
e.author = e.commit.creator_name e.author = e.commit.creator_name
elif e.etype == 'repo-create': elif e.etype == 'repo-create':
e.author = e.creator e.author = e.creator
else: else:
e.author = e.repo_owner e.author = e.repo_owner
if len(event_groups) == 0 or \ if len(event_groups) == 0 or \
len(event_groups) > 0 and e.date != event_groups[-1]['date']: len(event_groups) > 0 and e.date != event_groups[-1]['date']:
event_group = {} event_group = {}
@@ -1814,7 +1814,7 @@ def group_events_data(events):
def pdf_full_view(request): def pdf_full_view(request):
'''For pdf view with pdf.js.''' '''For pdf view with pdf.js.'''
repo_id = request.GET.get('repo_id', '') repo_id = request.GET.get('repo_id', '')
obj_id = request.GET.get('obj_id', '') obj_id = request.GET.get('obj_id', '')
file_name = request.GET.get('file_name', '') file_name = request.GET.get('file_name', '')
@@ -1840,7 +1840,7 @@ def convert_cmmt_desc_link(request):
# perm check # perm check
if check_repo_access_permission(repo_id, request.user) is None: if check_repo_access_permission(repo_id, request.user) is None:
raise Http404 raise Http404
diff_result = seafserv_threaded_rpc.get_diff(repo_id, '', cmmt_id) diff_result = seafserv_threaded_rpc.get_diff(repo_id, '', cmmt_id)
if not diff_result: if not diff_result:
raise Http404 raise Http404
@@ -1849,7 +1849,7 @@ def convert_cmmt_desc_link(request):
if name not in d.name: if name not in d.name:
# skip to next diff_result if file/folder user clicked does not # skip to next diff_result if file/folder user clicked does not
# match the diff_result # match the diff_result
continue continue
if d.status == 'add' or d.status == 'mod': # Add or modify file if d.status == 'add' or d.status == 'mod': # Add or modify file
return HttpResponseRedirect(reverse('repo_view_file', args=[repo_id]) + \ return HttpResponseRedirect(reverse('repo_view_file', args=[repo_id]) + \
@@ -1879,7 +1879,7 @@ def toggle_modules(request):
referer = request.META.get('HTTP_REFERER', None) referer = request.META.get('HTTP_REFERER', None)
next = settings.SITE_ROOT if referer is None else referer next = settings.SITE_ROOT if referer is None else referer
username = request.user.username username = request.user.username
personal_wiki = request.POST.get('personal_wiki', 'off') personal_wiki = request.POST.get('personal_wiki', 'off')
if personal_wiki == 'on': if personal_wiki == 'on':

View File

@@ -9,6 +9,8 @@ from django.shortcuts import render_to_response
from django.template import RequestContext from django.template import RequestContext
from registration.backends import get_backend from registration.backends import get_backend
from seahub.settings import USER_PASSWORD_MIN_LENGTH, \
USER_STRONG_PASSWORD_REQUIRED, USER_PASSWORD_STRENGTH_LEVEL
def activate(request, backend, def activate(request, backend,
template_name='registration/activate.html', template_name='registration/activate.html',
@@ -204,5 +206,8 @@ def register(request, backend, success_url=None, form_class=None,
form = form_class(initial={'email': src}) form = form_class(initial={'email': src})
return render_to_response(template_name, return render_to_response(template_name,
{ 'form': form }, { 'form': form,
context_instance=context) 'min_len': USER_PASSWORD_MIN_LENGTH,
'strong_pwd_required': USER_STRONG_PASSWORD_REQUIRED,
'level': USER_PASSWORD_STRENGTH_LEVEL,
}, context_instance=context)