1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-09 19:07:40 +00:00

[sysadmin-log] Show file-audit/update & permission log

This commit is contained in:
lian 2015-03-25 13:36:24 +08:00
parent 2ee4d9a16c
commit 7b51afe600
24 changed files with 358 additions and 87 deletions

View File

@ -1848,6 +1848,7 @@ textarea:-moz-placeholder {/* for FF */
.displayed-op .op:hover { .displayed-op .op:hover {
text-decoration:none; text-decoration:none;
} }
.audit-item .audit-select-hidden,
.repo-file-list .hidden-op { .repo-file-list .hidden-op {
position:absolute; position:absolute;
background:#fff; background:#fff;
@ -1856,6 +1857,7 @@ textarea:-moz-placeholder {/* for FF */
border-radius:5px; border-radius:5px;
z-index:10; z-index:10;
} }
.audit-select-hidden li a,
.hidden-op li a { .hidden-op li a {
display:block; display:block;
padding:0 12px; padding:0 12px;
@ -1864,6 +1866,13 @@ textarea:-moz-placeholder {/* for FF */
width:500px; width:500px;
padding:10px 20px; padding:10px 20px;
} }
.perm-dir-tree-cont {
padding:5px;
height:100px;
overflow:auto;
border:1px solid #eee;
margin:5px 0 10px;
}
.file-tree-cont, .dir-tree-cont { .file-tree-cont, .dir-tree-cont {
padding:5px; padding:5px;
height:280px; height:280px;
@ -3347,6 +3356,11 @@ textarea:-moz-placeholder {/* for FF */
} }
/* repo setting */ /* repo setting */
.user-perm-add-tr input,
.group-perm-add-tr input {
padding:2px 5px;
}
.user-perm-add-perm, .user-perm-add-perm,
.group-perm-add-perm, .group-perm-add-perm,
.perm-toggle-select, .perm-toggle-select,
@ -3368,3 +3382,25 @@ textarea:-moz-placeholder {/* for FF */
#perm-add-jstree-wrap { #perm-add-jstree-wrap {
background:#fcfcfc; background:#fcfcfc;
} }
/* file audit*/
.audit-show-select {
font-weight: normal;
font-size: 13px;
padding-right: 2px;
}
#audit-unselect-op div {
display: inline-block;
height: 20px;
border: 1px solid #ccc;
background: #f2f2f2;
cursor: pointer;
}
#audit-unselect-op span {
margin-right: 5px;
}
#audit-unselect-op a {
margin: 0 8px;
text-decoration: none;
font-size: 14px;
}

View File

@ -26,7 +26,6 @@ from django.http import HttpResponse, Http404
from django.template import RequestContext from django.template import RequestContext
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.utils import timezone
from .throttling import ScopedRateThrottle from .throttling import ScopedRateThrottle
from .authentication import TokenAuthentication from .authentication import TokenAuthentication
@ -63,6 +62,7 @@ from seahub.utils import gen_file_get_url, gen_token, gen_file_upload_url, \
get_org_user_events get_org_user_events
from seahub.utils.star import star_file, unstar_file from seahub.utils.star import star_file, unstar_file
from seahub.utils.file_types import IMAGE, DOCUMENT from seahub.utils.file_types import IMAGE, DOCUMENT
from seahub.utils.timeutils import utc_to_local
from seahub.views import validate_owner, is_registered_user, \ from seahub.views import validate_owner, is_registered_user, \
group_events_data, get_diff, create_default_library, get_owned_repo_list, \ group_events_data, get_diff, create_default_library, get_owned_repo_list, \
list_inner_pub_repos, get_virtual_repos_by_owner, check_folder_permission list_inner_pub_repos, get_virtual_repos_by_owner, check_folder_permission
@ -2549,12 +2549,6 @@ class EventsView(APIView):
else: else:
d['author'] = e.repo_owner d['author'] = e.repo_owner
def utc_to_local(dt):
tz = timezone.get_default_timezone()
utc = dt.replace(tzinfo=timezone.utc)
local = timezone.make_naive(utc, tz)
return local
epoch = datetime.datetime(1970, 1, 1) epoch = datetime.datetime(1970, 1, 1)
local = utc_to_local(e.timestamp) local = utc_to_local(e.timestamp)
time_diff = local - epoch time_diff = local - epoch

View File

@ -49,7 +49,7 @@
{% endif %} {% endif %}
</td> </td>
<td>{{ repo.owner|email2nickname }}</td> <td>{{ repo.owner|email2nickname }}</td>
<td data-id="{{ repo.props.id }}" data-owner="{{ repo.owner }}" data-name="{{ repo.props.name }}"> <td data-id="{{ repo.props.id }}" data-owner="{{ repo.owner }}" data-name="{{ repo.props.name }}" data-perm="{{ repo.user_perm }}">
{% if is_staff or repo.share_from_me %} {% if is_staff or repo.share_from_me %}
<img src="{{MEDIA_URL}}img/rm.png" alt="" class="cancel-share op-icon vh" title="{% trans "Unshare" %}" /> <img src="{{MEDIA_URL}}img/rm.png" alt="" class="cancel-share op-icon vh" title="{% trans "Unshare" %}" />
{% endif %} {% endif %}
@ -141,8 +141,9 @@ $('.cancel-share').click(function() {
var btn_ct = $(this).parent(), var btn_ct = $(this).parent(),
repo_id = btn_ct.data('id'), repo_id = btn_ct.data('id'),
repo_owner = btn_ct.attr('data-owner'), repo_owner = btn_ct.attr('data-owner'),
repo_perm = btn_ct.attr('data-perm'),
repo_name = btn_ct.attr('data-name'); repo_name = btn_ct.attr('data-name');
$(this).data('url', '{% url 'repo_remove_share' %}?repo_id=' + e(repo_id) + '&from=' + e(repo_owner) + '&gid={{ group.id }}').attr('data-target', repo_name); $(this).data('url', '{% url 'repo_remove_share' %}?repo_id=' + e(repo_id) + '&from=' + e(repo_owner) + '&gid={{ group.id }}' + '&permission=' + e(repo_perm)).attr('data-target', repo_name);
}); });
addConfirmTo($('.cancel-share'), { addConfirmTo($('.cancel-share'), {
'title': "{% trans "Unshare Library" %}", 'title': "{% trans "Unshare Library" %}",

View File

@ -427,6 +427,11 @@ PREVIEW_DEFAULT_SIZE = '100'
# for origin image file: size(MB) # for origin image file: size(MB)
THUMBNAIL_IMAGE_SIZE_LIMIT = 30 THUMBNAIL_IMAGE_SIZE_LIMIT = 30
#####################
# Folder Permission #
#####################
ENABLE_FOLDER_PERM = False
################# #################
# Email sending # # Email sending #
################# #################

View File

@ -40,14 +40,14 @@
<td>{{ repo.props.repo_desc }}</td> <td>{{ repo.props.repo_desc }}</td>
<td> <td>
{% if repo.props.share_type == 'group' %} {% if repo.props.share_type == 'group' %}
<a href="{% url 'repo_remove_share' %}?repo_id={{ repo.props.repo_id }}&from={{ request.user.username|urlencode }}&gid={{ repo.props.group_id }}" class="op-icon vh" title="{% trans "Unshare"%}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a> <a href="{% url 'repo_remove_share' %}?repo_id={{ repo.props.repo_id }}&from={{ request.user.username|urlencode }}&gid={{ repo.props.group_id }}&permission={{ repo.props.permission }}" class="op-icon vh" title="{% trans "Unshare"%}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a>
{% endif %} {% endif %}
{% if repo.props.share_type == 'personal' %} {% if repo.props.share_type == 'personal' %}
<a href="{% url 'repo_remove_share' %}?repo_id={{ repo.props.repo_id }}&from={{ request.user.username|urlencode }}&to={{ repo.props.user|urlencode }}" class="op-icon vh" title="{% trans "Unshare"%}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a> <a href="{% url 'repo_remove_share' %}?repo_id={{ repo.props.repo_id }}&from={{ request.user.username|urlencode }}&to={{ repo.props.user|urlencode }}&permission={{ repo.props.permission }}" class="op-icon vh" title="{% trans "Unshare"%}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a>
{% endif %} {% endif %}
{% if repo.props.share_type == 'public' %} {% if repo.props.share_type == 'public' %}
{% if not org %} {% if not org %}
<a href="{% url 'unsetinnerpub' repo.repo_id %}" class="op-icon vh" title="{% trans "Unshare"%}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a> <a href="{% url 'unsetinnerpub' repo.repo_id %}?permission={{ repo.props.permission }}" class="op-icon vh" title="{% trans "Unshare"%}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a>
{% else %} {% else %}
<a href="{{ SITE_ROOT }}organizations/{{ org.url_prefix }}/innerpubrepo/unset/{{ repo.props.repo_id }}" class="op-icon vh" title="{% trans "Unshare" %}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a> <a href="{{ SITE_ROOT }}organizations/{{ org.url_prefix }}/innerpubrepo/unset/{{ repo.props.repo_id }}" class="op-icon vh" title="{% trans "Unshare" %}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a>
{% endif %} {% endif %}
@ -92,10 +92,8 @@ $('.share-permission-select').change(function() {
success: function(data) { success: function(data) {
if (data['success']) { if (data['success']) {
feedback("{% trans "Edit succeeded" %}", 'success'); feedback("{% trans "Edit succeeded" %}", 'success');
select.prev().children('.share-permission-cur-value').html(select.children('option[value="' +select.val() + '"]').text()); location.reload(true);
} }
select.addClass('hide');
select.prev().removeClass('hide');
}, },
error: function() { error: function() {
feedback("{% trans "Edit failed." %}", 'error'); feedback("{% trans "Edit failed." %}", 'error');

View File

@ -44,13 +44,13 @@
<td>{{ repo.props.repo_desc }}</td> <td>{{ repo.props.repo_desc }}</td>
<td> <td>
{% if repo.props.share_type == 'group' %} {% if repo.props.share_type == 'group' %}
<a href="{% url 'repo_remove_share' %}?repo_id={{ repo.props.repo_id }}&from={{ request.user.username|urlencode }}&gid={{ repo.props.group_id }}" class="cancel-share op-icon vh" title="{% trans "Unshare"%}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a> <a href="{% url 'repo_remove_share' %}?repo_id={{ repo.props.repo_id }}&from={{ request.user.username|urlencode }}&gid={{ repo.props.group_id }}&permission={{ repo.props.permission }}" class="cancel-share op-icon vh" title="{% trans "Unshare"%}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a>
{% endif %} {% endif %}
{% if repo.props.share_type == 'personal' %} {% if repo.props.share_type == 'personal' %}
<a href="{% url 'repo_remove_share' %}?repo_id={{ repo.props.repo_id }}&from={{ request.user.username|urlencode }}&to={{ repo.props.user|urlencode }}" class="cancel-share op-icon vh" title="{% trans "Unshare"%}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a> <a href="{% url 'repo_remove_share' %}?repo_id={{ repo.props.repo_id }}&from={{ request.user.username|urlencode }}&to={{ repo.props.user|urlencode }}&permission={{ repo.props.permission }}" class="cancel-share op-icon vh" title="{% trans "Unshare"%}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a>
{% endif %} {% endif %}
{% if repo.props.share_type == 'public' %} {% if repo.props.share_type == 'public' %}
<a href="{% url 'unsetinnerpub' repo.repo_id %}" class="cancel-share op-icon vh" title="{% trans "Unshare"%}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a> <a href="{% url 'unsetinnerpub' repo.repo_id %}?permission={{ repo.props.permission }}" class="cancel-share op-icon vh" title="{% trans "Unshare"%}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a>
{% endif %} {% endif %}
</td> </td>
</tr> </tr>

View File

@ -56,9 +56,9 @@
<span data="{{ repo.repo_id }}" class="icon-cloud-download download-btn op-icon vh" title="{% trans "Download and Sync" %}"></span> <span data="{{ repo.repo_id }}" class="icon-cloud-download download-btn op-icon vh" title="{% trans "Download and Sync" %}"></span>
{% endif %} {% endif %}
{% if repo.share_in %} {% if repo.share_in %}
<span data-url="{% url 'repo_remove_share' %}?repo_id={{ repo.repo_id }}&from={{ repo.user|urlencode }}&to={{ request.user.username|urlencode }}" data-target="{{repo.repo_name}}" class="icon-trash unshare-btn op-icon vh" title="{% trans "Leave Share" %}"></span> <span data-url="{% url 'repo_remove_share' %}?repo_id={{ repo.repo_id }}&from={{ repo.user|urlencode }}&to={{ request.user.username|urlencode }}&permission={{ repo.permission }}" data-target="{{repo.repo_name}}" class="icon-trash unshare-btn op-icon vh" title="{% trans "Leave Share" %}"></span>
{% else %} {% else %}
<span data-url="{% url 'repo_remove_share' %}?repo_id={{ repo.props.repo_id }}&from={{ request.user.username|urlencode }}&to={{ repo.props.user|urlencode }}" data-target="{{repo.repo_name}}" class="icon-trash unshare-btn op-icon vh" title="{% trans "Unshare"%}"></span> <span data-url="{% url 'repo_remove_share' %}?repo_id={{ repo.props.repo_id }}&from={{ request.user.username|urlencode }}&to={{ repo.props.user|urlencode }}&permission={{ repo.permission }}" data-target="{{repo.repo_name}}" class="icon-trash unshare-btn op-icon vh" title="{% trans "Unshare"%}"></span>
{% endif %} {% endif %}
</td> </td>
</tr> </tr>

View File

@ -40,8 +40,8 @@ from seahub.views import is_registered_user, check_repo_access_permission, \
from seahub.utils import render_permission_error, string2list, render_error, \ from seahub.utils import render_permission_error, string2list, render_error, \
gen_token, gen_shared_link, gen_shared_upload_link, gen_dir_share_link, \ gen_token, gen_shared_link, gen_shared_upload_link, gen_dir_share_link, \
gen_file_share_link, IS_EMAIL_CONFIGURED, check_filename_with_rename, \ gen_file_share_link, IS_EMAIL_CONFIGURED, check_filename_with_rename, \
is_valid_username, send_html_email, is_org_context, \ is_valid_username, send_html_email, is_org_context, normalize_file_path, \
normalize_file_path, normalize_dir_path normalize_dir_path, send_perm_audit_msg, get_origin_repo_info
from seahub.settings import SITE_ROOT, REPLACE_FROM_EMAIL, ADD_REPLY_TO_HEADER from seahub.settings import SITE_ROOT, REPLACE_FROM_EMAIL, ADD_REPLY_TO_HEADER
# Get an instance of a logger # Get an instance of a logger
@ -253,8 +253,18 @@ def share_repo(request):
if is_valid_username(share_to): if is_valid_username(share_to):
share_to_users.append(share_to) share_to_users.append(share_to)
origin_repo_id, origin_path = get_origin_repo_info(repo.id)
if origin_repo_id is not None:
perm_repo_id = origin_repo_id
perm_path = origin_path
else:
perm_repo_id = repo.id
perm_path = '/'
if share_to_all: if share_to_all:
share_to_public(request, repo, permission) share_to_public(request, repo, permission)
send_perm_audit_msg('add-repo-perm', username, 'all', \
perm_repo_id, perm_path, permission)
if not check_user_share_quota(username, repo, users=share_to_users, if not check_user_share_quota(username, repo, users=share_to_users,
groups=share_to_groups): groups=share_to_groups):
@ -266,11 +276,15 @@ def share_repo(request):
for group in share_to_groups: for group in share_to_groups:
share_to_group(request, repo, group, permission) share_to_group(request, repo, group, permission)
send_perm_audit_msg('add-repo-perm', username, group.id, \
perm_repo_id, perm_path, permission)
for email in share_to_users: for email in share_to_users:
# Add email to contacts. # Add email to contacts.
mail_sended.send(sender=None, user=request.user.username, email=email) mail_sended.send(sender=None, user=request.user.username, email=email)
share_to_user(request, repo, email, permission) share_to_user(request, repo, email, permission)
send_perm_audit_msg('add-repo-perm', username, email, \
perm_repo_id, perm_path, permission)
return HttpResponseRedirect(next) return HttpResponseRedirect(next)
@ -285,10 +299,23 @@ def repo_remove_share(request):
repo_id = request.GET.get('repo_id', '') repo_id = request.GET.get('repo_id', '')
group_id = request.GET.get('gid', '') group_id = request.GET.get('gid', '')
from_email = request.GET.get('from', '') from_email = request.GET.get('from', '')
if not is_valid_username(from_email): perm = request.GET.get('permission', None)
if not is_valid_username(from_email) or perm is None:
return render_error(request, _(u'Argument is not valid')) return render_error(request, _(u'Argument is not valid'))
username = request.user.username username = request.user.username
repo = seafile_api.get_repo(repo_id)
if not repo:
return render_error(request, _(u'Library does not exist'))
origin_repo_id, origin_path = get_origin_repo_info(repo.id)
if origin_repo_id is not None:
perm_repo_id = origin_repo_id
perm_path = origin_path
else:
perm_repo_id = repo.id
perm_path = '/'
# if request params don't have 'gid', then remove repos that share to # if request params don't have 'gid', then remove repos that share to
# to other person; else, remove repos that share to groups # to other person; else, remove repos that share to groups
if not group_id: if not group_id:
@ -304,6 +331,8 @@ def repo_remove_share(request):
org_remove_share(org_id, repo_id, from_email, to_email) org_remove_share(org_id, repo_id, from_email, to_email)
else: else:
seaserv.remove_share(repo_id, from_email, to_email) seaserv.remove_share(repo_id, from_email, to_email)
send_perm_audit_msg('delete-repo-perm', from_email, to_email, \
perm_repo_id, perm_path, perm)
else: else:
try: try:
group_id = int(group_id) group_id = int(group_id)
@ -323,6 +352,8 @@ def repo_remove_share(request):
del_org_group_repo(repo_id, org_id, group_id) del_org_group_repo(repo_id, org_id, group_id)
else: else:
seafile_api.unset_group_repo(repo_id, group_id, from_email) seafile_api.unset_group_repo(repo_id, group_id, from_email)
send_perm_audit_msg('delete-repo-perm', from_email, group_id, \
perm_repo_id, perm_path, perm)
messages.success(request, _('Successfully removed share')) messages.success(request, _('Successfully removed share'))
@ -566,6 +597,19 @@ def share_permission_admin(request):
permission = form.cleaned_data['permission'] permission = form.cleaned_data['permission']
from_email = request.user.username from_email = request.user.username
repo = seafile_api.get_repo(repo_id)
if not repo:
return render_error(request, _(u'Library does not exist'))
origin_repo_id, origin_path = get_origin_repo_info(repo.id)
if origin_repo_id is not None:
perm_repo_id = origin_repo_id
perm_path = origin_path
else:
perm_repo_id = repo.id
perm_path = '/'
if share_type == 'personal': if share_type == 'personal':
if not is_valid_username(email_or_group): if not is_valid_username(email_or_group):
return HttpResponse(json.dumps({'success': False}), status=400, return HttpResponse(json.dumps({'success': False}), status=400,
@ -579,6 +623,9 @@ def share_permission_admin(request):
else: else:
seafile_api.set_share_permission(repo_id, from_email, seafile_api.set_share_permission(repo_id, from_email,
email_or_group, permission) email_or_group, permission)
send_perm_audit_msg('modify-repo-perm', from_email, \
email_or_group, perm_repo_id, perm_path, permission)
except SearpcError: except SearpcError:
return HttpResponse(json.dumps({'success': False}), status=500, return HttpResponse(json.dumps({'success': False}), status=500,
content_type=content_type) content_type=content_type)
@ -592,8 +639,12 @@ def share_permission_admin(request):
seaserv.seafserv_threaded_rpc.set_org_group_repo_permission( seaserv.seafserv_threaded_rpc.set_org_group_repo_permission(
org_id, int(email_or_group), repo_id, permission) org_id, int(email_or_group), repo_id, permission)
else: else:
seafile_api.set_group_repo_permission(int(email_or_group), group_id = int(email_or_group)
repo_id, permission) seafile_api.set_group_repo_permission(group_id,
repo_id,
permission)
send_perm_audit_msg('modify-repo-perm', from_email, \
group_id, perm_repo_id, perm_path, permission)
except SearpcError: except SearpcError:
return HttpResponse(json.dumps({'success': False}), status=500, return HttpResponse(json.dumps({'success': False}), status=500,
content_type=content_type) content_type=content_type)
@ -608,6 +659,8 @@ def share_permission_admin(request):
org_id, repo_id, permission) org_id, repo_id, permission)
else: else:
seafile_api.add_inner_pub_repo(repo_id, permission) seafile_api.add_inner_pub_repo(repo_id, permission)
send_perm_audit_msg('modify-repo-perm', from_email, 'all', \
perm_repo_id, perm_path, permission)
except SearpcError: except SearpcError:
return HttpResponse(json.dumps({'success': False}), status=500, return HttpResponse(json.dumps({'success': False}), status=500,
content_type=content_type) content_type=content_type)

View File

@ -38,7 +38,7 @@
<td>{{ repo.props.user|email2nickname }}</td> <td>{{ repo.props.user|email2nickname }}</td>
<td> <td>
{% if request.user.is_staff or repo.share_from_me %} {% if request.user.is_staff or repo.share_from_me %}
<img src="{{MEDIA_URL}}img/rm.png" alt="" data-href="{% url 'unsetinnerpub' repo.repo_id %}" class="cancel-share op-icon vh" title="{% trans "Unshare" %}" /> <img src="{{MEDIA_URL}}img/rm.png" alt="" data-href="{% url 'unsetinnerpub' repo.repo_id %}?permission={{repo.permission}}" class="cancel-share op-icon vh" title="{% trans "Unshare" %}" />
{% endif %} {% endif %}
</td> </td>
</tr> </tr>

View File

@ -23,8 +23,10 @@
{% if not repo.encrypted %} {% if not repo.encrypted %}
<li class="tab"><a href="{% url 'repo_shared_link' repo.id %}">{% trans "Shared Links" %}</a></li> <li class="tab"><a href="{% url 'repo_shared_link' repo.id %}">{% trans "Shared Links" %}</a></li>
{% endif %} {% endif %}
<li class="tab"><a href="{% url 'repo_share_manage' repo.id %}">{% trans "Sharing Management" %}</a></li> <li class="tab"><a href="{% url 'repo_share_manage' repo.id %}">{% trans "Sharing Permission" %}</a></li>
<li class="tab"><a href="{% url 'repo_folder_perm' repo.id %}">{% trans "SubFolder Permission" %}</a></li> {% if ENABLE_FOLDER_PERM %}
<li class="tab"><a href="{% url 'repo_folder_perm' repo.id %}">{% trans "Folder Permission" %}</a></li>
{% endif %}
</ul> </ul>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -23,8 +23,10 @@
{% if not repo.encrypted %} {% if not repo.encrypted %}
<li class="tab"><a href="{% url 'repo_shared_link' repo.id %}">{% trans "Shared Links" %}</a></li> <li class="tab"><a href="{% url 'repo_shared_link' repo.id %}">{% trans "Shared Links" %}</a></li>
{% endif %} {% endif %}
<li class="tab"><a href="{% url 'repo_share_manage' repo.id %}">{% trans "Sharing Management" %}</a></li> <li class="tab"><a href="{% url 'repo_share_manage' repo.id %}">{% trans "Sharing Permission" %}</a></li>
<li class="tab"><a href="{% url 'repo_folder_perm' repo.id %}">{% trans "SubFolder Permission" %}</a></li> {% if ENABLE_FOLDER_PERM %}
<li class="tab"><a href="{% url 'repo_folder_perm' repo.id %}">{% trans "Folder Permission" %}</a></li>
{% endif %}
</ul> </ul>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -23,8 +23,8 @@
{% if not repo.encrypted %} {% if not repo.encrypted %}
<li class="tab"><a href="{% url 'repo_shared_link' repo.id %}">{% trans "Shared Links" %}</a></li> <li class="tab"><a href="{% url 'repo_shared_link' repo.id %}">{% trans "Shared Links" %}</a></li>
{% endif %} {% endif %}
<li class="tab"><a href="{% url 'repo_share_manage' repo.id %}">{% trans "Sharing Management" %}</a></li> <li class="tab"><a href="{% url 'repo_share_manage' repo.id %}">{% trans "Sharing Permission" %}</a></li>
<li class="tab tab-cur"><a href="{% url 'repo_folder_perm' repo.id %}">{% trans "SubFolder Permission" %}</a></li> <li class="tab tab-cur"><a href="{% url 'repo_folder_perm' repo.id %}">{% trans "Folder Permission" %}</a></li>
</ul> </ul>
</div> </div>
{% endblock %} {% endblock %}
@ -33,7 +33,7 @@
<div class="lib-setting"> <div class="lib-setting">
<h2>{% trans "Library Settings" %}</h2> <h2>{% trans "Library Settings" %}</h2>
<h3>{% trans "Folder Permission" %}</h3> <h3>{% trans "Folder Permission" %}</h3>
<p>{% trans "View and manage all the folder permissions in this library." %}</p> <p>{% trans "View and manage all folder permissions in this library." %}</p>
<div id="tabs" class="tab-tabs"> <div id="tabs" class="tab-tabs">
<div class="hd ovhd"> <div class="hd ovhd">
<ul class="tab-tabs-nav fleft"> <ul class="tab-tabs-nav fleft">
@ -54,9 +54,9 @@
<th width="10%"></th> <th width="10%"></th>
</tr> </tr>
<tr class="user-perm-add-tr hide"> <tr class="user-perm-add-tr hide">
<td><input id="perm-add-user" placeholder=" {% trans "Email"%}"></input></td> <td><input id="perm-add-user" placeholder="{% trans "Email"%}"></input></td>
<td> <td>
<input class="user-perm-add-folder-name" placeholder=" {% trans "Select a folder"%}"></input><img src="{{ MEDIA_URL }}img/add.png" alt="" title="Select Folder" class="perm-add-select-folder add vam" /> <input class="user-perm-add-folder-name" placeholder="{% trans "Select a folder"%}"></input><img src="{{ MEDIA_URL }}img/add.png" alt="" title="Select Folder" class="perm-add-select-folder add vam" />
<input class="user-perm-add-folder-path" type="hidden"></input> <input class="user-perm-add-folder-path" type="hidden"></input>
</td> </td>
<td> <td>
@ -71,7 +71,7 @@
</tr> </tr>
{% if user_folder_perms %} {% if user_folder_perms %}
{% for perm in user_folder_perms %} {% for perm in user_folder_perms %}
<tr class="perm-item" data-user="{{ perm.user }}" data-path="{{ perm.path }}"> <tr class="perm-item" data-user="{{ perm.user }}" data-path="{{ perm.path }}" data-perm="{{ perm.permission }}">
<td><a href="{% url 'user_profile' perm.user %}">{{ perm.user }}</a></td> <td><a href="{% url 'user_profile' perm.user %}">{{ perm.user }}</a></td>
<td><span><a href="{{ perm.folder_link }}">{{ perm.folder_name }}</a></span></td> <td><span><a href="{{ perm.folder_link }}">{{ perm.folder_name }}</a></span></td>
<td> <td>
@ -107,11 +107,11 @@
</tr> </tr>
<tr class="group-perm-add-tr hide"> <tr class="group-perm-add-tr hide">
<td> <td>
<input id="perm-add-grp-name" placeholder=" {% trans "Group"%}"></input> <input id="perm-add-grp-name" placeholder="{% trans "Group"%}"></input>
<input id="perm-add-grp-id" type="hidden"></input> <input id="perm-add-grp-id" type="hidden"></input>
</td> </td>
<td> <td>
<input class="group-perm-add-folder-name" placeholder=" {% trans "Select a folder"%}"></input><img src="{{ MEDIA_URL }}img/add.png" alt="" title="Select Folder" class="perm-add-select-folder add vam" /> <input class="group-perm-add-folder-name" placeholder="{% trans "Select a folder"%}"></input><img src="{{ MEDIA_URL }}img/add.png" alt="" title="Select Folder" class="perm-add-select-folder add vam" />
<input class="group-perm-add-folder-path" type="hidden"></input> <input class="group-perm-add-folder-path" type="hidden"></input>
</td> </td>
<td> <td>
@ -126,7 +126,7 @@
</tr> </tr>
{% if group_folder_perms %} {% if group_folder_perms %}
{% for perm in group_folder_perms %} {% for perm in group_folder_perms %}
<tr class="perm-item" data-group_id="{{ perm.group_id }}" data-path="{{ perm.path }}"> <tr class="perm-item" data-group_id="{{ perm.group_id }}" data-path="{{ perm.path }}" data-perm="{{ perm.permission }}">
<td><a href="{% url 'group_info' perm.group_id %}">{{ perm.group_name }}</a></td> <td><a href="{% url 'group_info' perm.group_id %}">{{ perm.group_name }}</a></td>
<td> <td>
<span><a href="{{ perm.folder_link }}">{{ perm.folder_name }}</a></span> <span><a href="{{ perm.folder_link }}">{{ perm.folder_name }}</a></span>
@ -157,7 +157,7 @@
<div id="perm-add-jstree-wrap" class="hide"> <div id="perm-add-jstree-wrap" class="hide">
<h3>{% trans "Please selecte a directory" %}</h3> <h3>{% trans "Please selecte a directory" %}</h3>
<img src="{{MEDIA_URL}}img/loading-icon.gif" alt="" class="loading-tip" /> <img src="{{MEDIA_URL}}img/loading-icon.gif" alt="" class="loading-tip" />
<div id="perm-add-jstree" data-repo_id="{{repo.id}}"></div> <div id="perm-add-jstree" class="perm-dir-tree-cont" data-repo_id="{{repo.id}}"></div>
<button class="perm-add-jstree-select">{% trans "Select" %}</button> <button class="perm-add-jstree-select">{% trans "Select" %}</button>
<button class="simplemodal-close">{% trans "Cancel"%}</button><br /> <button class="simplemodal-close">{% trans "Cancel"%}</button><br />
<span class="error hide"></span> <span class="error hide"></span>
@ -197,7 +197,7 @@ group_list.push({value:group_id, label:group_name});
user_input.bind('autocompleteopen', function(e, ui) { user_input.bind('autocompleteopen', function(e, ui) {
var widget = user_input.autocomplete('widget'); var widget = user_input.autocomplete('widget');
widget.css({'max-width':user_input.width() - 2 * parseInt(widget.css('padding')), 'max-height':$(window).height() + $(window).scrollTop() - user_input.offset().top - user_input.outerHeight()}); widget.css({'max-width':user_input.width() + 2 * parseInt(widget.css('padding-top')), 'max-height':$(window).height() + $(window).scrollTop() - user_input.offset().top - user_input.outerHeight()});
}) })
.autocomplete({ .autocomplete({
source: user_list source: user_list
@ -205,7 +205,7 @@ user_input.bind('autocompleteopen', function(e, ui) {
group_input.bind('autocompleteopen', function(e, ui) { group_input.bind('autocompleteopen', function(e, ui) {
var widget = group_input.autocomplete('widget'); var widget = group_input.autocomplete('widget');
widget.css({'max-width':group_input.width() - 2 * parseInt(widget.css('padding')), 'max-height':$(window).height() + $(window).scrollTop() - group_input.offset().top - group_input.outerHeight()}); widget.css({'max-width':group_input.width() + 2 * parseInt(widget.css('padding-top')), 'max-height':$(window).height() + $(window).scrollTop() - group_input.offset().top - group_input.outerHeight()});
}) })
.autocomplete({ .autocomplete({
source: group_list, source: group_list,
@ -250,15 +250,16 @@ $('.perm-toggle-select').on('change', function() {
}); });
$('.perm-delete-btn').on('click', function() { $('.perm-delete-btn').on('click', function() {
var perm_item = $(this).parents('.perm-item'), var perm_item = $(this).parents('.perm-item'),
perm = perm_item.attr('data-perm'),
path = perm_item.attr('data-path'); path = perm_item.attr('data-path');
if (is_user_tab == true) { if (is_user_tab == true) {
var user = perm_item.attr('data-user'), var user = perm_item.attr('data-user'),
data = { 'user': user, 'path': path }, data = { 'user': user, 'path': path, 'perm': perm },
url = '{% url 'remove_user_folder_permission' repo.id %}'; url = '{% url 'remove_user_folder_permission' repo.id %}';
} else { } else {
var group_id = perm_item.attr('data-group_id'), var group_id = perm_item.attr('data-group_id'),
data = { 'group_id': group_id, 'path': path }, data = { 'group_id': group_id, 'path': path, 'perm': perm},
url = '{% url 'remove_group_folder_permission' repo.id %}'; url = '{% url 'remove_group_folder_permission' repo.id %}';
} }
@ -329,7 +330,7 @@ function selectClickHandler(event) {
} }
if (path_array.length == 1) { if (path_array.length == 1) {
path = path_array.shift(); path = path_array.shift();
folder_name.val('Root Directory'); folder_name.val('/');
} else { } else {
var root = path_array.shift(), var root = path_array.shift(),
path = root + path_array.join('/'), path = root + path_array.join('/'),

View File

@ -23,8 +23,10 @@
{% if not repo.encrypted %} {% if not repo.encrypted %}
<li class="tab"><a href="{% url 'repo_shared_link' repo.id %}">{% trans "Shared Links" %}</a></li> <li class="tab"><a href="{% url 'repo_shared_link' repo.id %}">{% trans "Shared Links" %}</a></li>
{% endif %} {% endif %}
<li class="tab tab-cur"><a href="{% url 'repo_share_manage' repo.id %}">{% trans "Sharing Management" %}</a></li> <li class="tab tab-cur"><a href="{% url 'repo_share_manage' repo.id %}">{% trans "Sharing Permission" %}</a></li>
<li class="tab"><a href="{% url 'repo_folder_perm' repo.id %}">{% trans "SubFolder Permission" %}</a></li> {% if ENABLE_FOLDER_PERM %}
<li class="tab"><a href="{% url 'repo_folder_perm' repo.id %}">{% trans "Folder Permission" %}</a></li>
{% endif %}
</ul> </ul>
</div> </div>
{% endblock %} {% endblock %}
@ -33,7 +35,7 @@
<div class="lib-setting"> <div class="lib-setting">
<h2>{% trans "Library Settings" %}</h2> <h2>{% trans "Library Settings" %}</h2>
<h3>{% trans "Sharing Management" %}</h3> <h3>{% trans "Sharing Management" %}</h3>
<p>{% trans "View and manage all the share of this library." %}</p> <p>{% trans "View and manage sharing permissions of this library." %}</p>
<div id="tabs" class="tab-tabs"> <div id="tabs" class="tab-tabs">
<div class="hd ovhd"> <div class="hd ovhd">
@ -72,7 +74,7 @@
</select> </select>
</td> </td>
<td> <td>
<a href="{% url 'repo_remove_share' %}?repo_id={{ share.repo_id }}&from={{ request.user.username|urlencode }}&to={{ share.user|urlencode }}" class="cancel-share op-icon vh" title="{% trans "Unshare"%}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a> <a href="{% url 'repo_remove_share' %}?repo_id={{ share.repo_id }}&from={{ request.user.username|urlencode }}&to={{ share.user|urlencode }}&permission={{ share.permission }}" class="cancel-share op-icon vh" title="{% trans "Unshare"%}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -105,7 +107,7 @@
</select> </select>
</td> </td>
<td> <td>
<a href="{% url 'repo_remove_share' %}?repo_id={{ share.repo_id }}&from={{ request.user.username|urlencode }}&gid={{ share.group_id }}" class="cancel-share op-icon vh" title="{% trans "Unshare"%}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a> <a href="{% url 'repo_remove_share' %}?repo_id={{ share.repo_id }}&from={{ request.user.username|urlencode }}&gid={{ share.group_id }}&permission={{ share.permission }}" class="cancel-share op-icon vh" title="{% trans "Unshare"%}"><img src="{{MEDIA_URL}}img/rm.png" alt="" /></a>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -23,8 +23,10 @@
{% if not repo.encrypted %} {% if not repo.encrypted %}
<li class="tab tab-cur"><a href="{% url 'repo_shared_link' repo.id %}">{% trans "Shared Links" %}</a></li> <li class="tab tab-cur"><a href="{% url 'repo_shared_link' repo.id %}">{% trans "Shared Links" %}</a></li>
{% endif %} {% endif %}
<li class="tab"><a href="{% url 'repo_share_manage' repo.id %}">{% trans "Sharing Management" %}</a></li> <li class="tab"><a href="{% url 'repo_share_manage' repo.id %}">{% trans "Sharing Permission" %}</a></li>
<li class="tab"><a href="{% url 'repo_folder_perm' repo.id %}">{% trans "SubFolder Permission" %}</a></li> {% if ENABLE_FOLDER_PERM %}
<li class="tab"><a href="{% url 'repo_folder_perm' repo.id %}">{% trans "Folder Permission" %}</a></li>
{% endif %}
</ul> </ul>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -23,8 +23,10 @@
{% if not repo.encrypted %} {% if not repo.encrypted %}
<li class="tab"><a href="{% url 'repo_shared_link' repo.id %}">{% trans "Shared Links" %}</a></li> <li class="tab"><a href="{% url 'repo_shared_link' repo.id %}">{% trans "Shared Links" %}</a></li>
{% endif %} {% endif %}
<li class="tab"><a href="{% url 'repo_share_manage' repo.id %}">{% trans "Sharing Management" %}</a></li> <li class="tab"><a href="{% url 'repo_share_manage' repo.id %}">{% trans "Sharing Permission" %}</a></li>
<li class="tab"><a href="{% url 'repo_folder_perm' repo.id %}">{% trans "SubFolder Permission" %}</a></li> {% if ENABLE_FOLDER_PERM %}
<li class="tab"><a href="{% url 'repo_folder_perm' repo.id %}">{% trans "Folder Permission" %}</a></li>
{% endif %}
</ul> </ul>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -33,7 +33,7 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if not traffic_over_limit %} {% if not traffic_over_limit %}
<a href="{{ SITE_ROOT }}repo/{{ repo.id }}/{{ obj_id }}/?file_name={{ file_name|urlencode }}&op=download&t={{ shared_token }}&p={{path|urlencode}}" class="obv-btn">{% trans "Download" %} ({{file_size|filesizeformat}})</a> <a href="{% url 'download_file' repo.id obj_id%}?file_name={{ file_name|urlencode }}&t={{ shared_token }}&p={{path|urlencode}}" class="obv-btn">{% trans "Download" %} ({{file_size|filesizeformat}})</a>
{% endif %} {% endif %}
</div> </div>
{% include 'snippets/file_content_html.html' %} {% include 'snippets/file_content_html.html' %}

View File

@ -26,7 +26,7 @@
<span data="{{ repo.repo_id }}" class="icon-cloud-download download-btn op-icon vh" title="{% trans "Download and Sync" %}"></span> <span data="{{ repo.repo_id }}" class="icon-cloud-download download-btn op-icon vh" title="{% trans "Download and Sync" %}"></span>
{% endif %} {% endif %}
{% if repo.share_type == 'personal' %} {% if repo.share_type == 'personal' %}
<span data-url="{% url 'repo_remove_share' %}?repo_id={{ repo.repo_id }}&from={{ repo.user|urlencode }}&to={{ request.user.username|urlencode }}" data-target="{{repo.repo_name}}" class="icon-trash unshare-btn op-icon vh" title="{% trans "Leave Share" %}"></span> <span data-url="{% url 'repo_remove_share' %}?repo_id={{ repo.repo_id }}&from={{ repo.user|urlencode }}&to={{ request.user.username|urlencode }}&permission={{ repo.user_perm }}" data-target="{{repo.repo_name}}" class="icon-trash unshare-btn op-icon vh" title="{% trans "Leave Share" %}"></span>
{% endif %} {% endif %}
</td> </td>
</tr> </tr>

View File

@ -254,9 +254,13 @@ if getattr(settings, 'ENABLE_PAYMENT', False):
if getattr(settings, 'ENABLE_SYSADMIN_EXTRA', False): if getattr(settings, 'ENABLE_SYSADMIN_EXTRA', False):
from seahub_extra.sysadmin_extra.views import sys_login_admin from seahub_extra.sysadmin_extra.views import sys_login_admin, \
sys_log_file_audit, sys_log_file_update, sys_log_perm_audit
urlpatterns += patterns('', urlpatterns += patterns('',
url(r'^sys/loginadmin/', sys_login_admin, name='sys_login_admin'), url(r'^sys/loginadmin/', sys_login_admin, name='sys_login_admin'),
url(r'^sys/log/fileaudit/', sys_log_file_audit, name='sys_log_file_audit'),
url(r'^sys/log/fileupdate/', sys_log_file_update, name='sys_log_file_update'),
url(r'^sys/log/permaudit/', sys_log_perm_audit, name='sys_log_perm_audit'),
) )
if getattr(settings, 'MULTI_TENANCY', False): if getattr(settings, 'MULTI_TENANCY', False):

View File

@ -10,6 +10,7 @@ import tempfile
import locale import locale
import ConfigParser import ConfigParser
import mimetypes import mimetypes
import contextlib
from datetime import datetime from datetime import datetime
from urlparse import urlparse, urljoin from urlparse import urlparse, urljoin
@ -31,8 +32,8 @@ from django.views.static import serve as django_static_serve
from seahub.api2.models import Token, TokenV2 from seahub.api2.models import Token, TokenV2
import seaserv import seaserv
from seaserv import seafile_api from seaserv import seafile_api, send_message, seafserv_rpc, \
from seaserv import seafserv_rpc, seafserv_threaded_rpc, get_repo, get_commits,\ seafserv_threaded_rpc, get_repo, get_commits,\
CCNET_SERVER_ADDR, CCNET_SERVER_PORT, get_org_by_id, is_org_staff, \ CCNET_SERVER_ADDR, CCNET_SERVER_PORT, get_org_by_id, is_org_staff, \
get_org_id_by_group, get_personal_groups_by_user, \ get_org_id_by_group, get_personal_groups_by_user, \
list_personal_repos_by_owner, get_group_repos, \ list_personal_repos_by_owner, get_group_repos, \
@ -526,6 +527,14 @@ if EVENTS_CONFIG_FILE:
EVENTS_ENABLED = True EVENTS_ENABLED = True
SeafEventsSession = seafevents.init_db_session_class(EVENTS_CONFIG_FILE) SeafEventsSession = seafevents.init_db_session_class(EVENTS_CONFIG_FILE)
@contextlib.contextmanager
def _get_seafevents_session():
try:
session = SeafEventsSession()
yield session
finally:
session.close()
def _same_events(e1, e2): def _same_events(e1, e2):
"""Two events are equal should follow two rules: """Two events are equal should follow two rules:
1. event1.repo_id = event2.repo_id 1. event1.repo_id = event2.repo_id
@ -634,6 +643,48 @@ if EVENTS_CONFIG_FILE:
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)
def get_file_audit_events(email, org_id, repo_id, start, limit):
"""Return file audit events list. (If no file audit, return 'None')
For example:
``get_file_audit_events(email, org_id, repo_id, 0, 10)`` returns the first 10
events.
``get_file_audit_events(email, org_id, repo_id, 5, 10)`` returns the 6th through
15th events.
"""
with _get_seafevents_session() as session:
events = seafevents.get_file_audit_events(session, email, org_id, repo_id, start, limit)
return events if events else None
def get_file_update_events(email, org_id, repo_id, start, limit):
"""Return file update events list. (If no file update, return 'None')
For example:
``get_file_update_events(email, org_id, repo_id, 0, 10)`` returns the first 10
events.
``get_file_update_events(email, org_id, repo_id, 5, 10)`` returns the 6th through
15th events.
"""
with _get_seafevents_session() as session:
events = seafevents.get_file_update_events(session, email, org_id, repo_id, start, limit)
return events if events else None
def get_perm_audit_events(email, org_id, repo_id, start, limit):
"""Return repo perm events list. (If no repo perm, return 'None')
For example:
``get_repo_perm_events(email, org_id, repo_id, 0, 10)`` returns the first 10
events.
``get_repo_perm_events(email, org_id, repo_id, 5, 10)`` returns the 6th through
15th events.
"""
with _get_seafevents_session() as session:
events = seafevents.get_perm_audit_events(session, email, org_id, repo_id, start, limit)
return events if events else None
else: else:
EVENTS_ENABLED = False EVENTS_ENABLED = False
def get_user_events(): def get_user_events():
@ -1192,3 +1243,31 @@ def clear_token(username):
Token.objects.filter(user = username).delete() Token.objects.filter(user = username).delete()
TokenV2.objects.filter(user = username).delete() TokenV2.objects.filter(user = username).delete()
seafile_api.delete_repo_tokens_by_email(username) seafile_api.delete_repo_tokens_by_email(username)
def send_perm_audit_msg(etype, from_user, to, repo_id, path, perm):
"""Send repo permission audit msg.
Arguments:
- `request`:
- `repo`:
- `obj_id`:
- `dl_type`: web or api
"""
msg = 'perm-update\t%s\t%s\t%s\t%s\t%s\t%s' % \
(etype, from_user, to, repo_id, path, perm)
msg_utf8 = msg.encode('utf-8')
try:
send_message('seahub.stats', msg_utf8)
except Exception as e:
logger.error("Error when sending perm-audit-%s message: %s" %
(etype, str(e)))
def get_origin_repo_info(repo_id):
repo = seafile_api.get_repo(repo_id)
if repo.origin_repo_id is not None:
origin_repo_id = repo.origin_repo_id
origin_path = repo.origin_path
return (origin_repo_id, origin_path)
return (None, None)

View File

@ -1,11 +1,15 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
import seaserv import seaserv
from seaserv import seafile_api from seaserv import seafile_api
from seahub.utils import EMPTY_SHA1, is_valid_username from seahub.utils import EMPTY_SHA1
from seahub.views import check_repo_access_permission from seahub.views import check_repo_access_permission
from seahub.base.accounts import User
logger = logging.getLogger(__name__)
def list_dir_by_path(cmmt, path): def list_dir_by_path(cmmt, path):
if cmmt.root_id == EMPTY_SHA1: if cmmt.root_id == EMPTY_SHA1:
@ -13,11 +17,11 @@ def list_dir_by_path(cmmt, path):
else: else:
return seafile_api.list_dir_by_commit_and_path(cmmt.repo_id, cmmt.id, path) return seafile_api.list_dir_by_commit_and_path(cmmt.repo_id, cmmt.id, path)
def check_user_folder_perm_args(request_user, repo_id, path, user, perm=None): def check_user_folder_perm_args(from_user, repo_id, path, to_user, perm=None):
if not seafile_api.get_repo(repo_id): if not seafile_api.get_repo(repo_id):
return {'error': _(u'Library does not exist.'), 'status': 400} return {'error': _(u'Library does not exist.'), 'status': 400}
if check_repo_access_permission(repo_id, request_user) != 'rw': if check_repo_access_permission(repo_id, from_user) != 'rw':
return {'error': _('Permission denied'), 'status': 403} return {'error': _('Permission denied'), 'status': 403}
if perm is not None: if perm is not None:
@ -34,16 +38,21 @@ def check_user_folder_perm_args(request_user, repo_id, path, user, perm=None):
if path != '/' and path.endswith('/'): if path != '/' and path.endswith('/'):
return {'error': _('Path should NOT ends with "/"'), 'status': 400} return {'error': _('Path should NOT ends with "/"'), 'status': 400}
if user and not is_valid_username(user): try:
return {'error': _('Invalid username'), 'status': 400} user = User.objects.get(email = to_user)
except User.DoesNotExist:
user = None
if user is None:
return {'error': _('Invalid username, should be a user already registered'), 'status': 400}
return {'success': True} return {'success': True}
def check_group_folder_perm_args(request_user, repo_id, path, group_id, perm = None): def check_group_folder_perm_args(from_user, repo_id, path, group_id, perm = None):
if not seafile_api.get_repo(repo_id): if not seafile_api.get_repo(repo_id):
return {'error': _(u'Library does not exist.'), 'status': 400} return {'error': _(u'Library does not exist.'), 'status': 400}
if check_repo_access_permission(repo_id, request_user) != 'rw': if check_repo_access_permission(repo_id, from_user) != 'rw':
return {'error': _('Permission denied'), 'status': 403} return {'error': _('Permission denied'), 'status': 403}
if perm is not None: if perm is not None:
@ -60,7 +69,8 @@ def check_group_folder_perm_args(request_user, repo_id, path, group_id, perm = N
if path != '/' and path.endswith('/'): if path != '/' and path.endswith('/'):
return {'error': _('Path should NOT ends with "/"'), 'status': 400} return {'error': _('Path should NOT ends with "/"'), 'status': 400}
if group_id and not seaserv.get_group(group_id): if not seaserv.get_group(group_id):
return {'error': _('Invalid group'), 'status': 400} return {'error': _('Invalid group'), 'status': 400}
return {'success': True} return {'success': True}

View File

@ -26,4 +26,10 @@ def value_to_db_datetime(value):
# MySQL doesn't support microseconds # MySQL doesn't support microseconds
return six.text_type(value.replace(microsecond=0)) return six.text_type(value.replace(microsecond=0))
def utc_to_local(dt):
# change from UTC timezone to current seahub timezone
tz = timezone.get_default_timezone()
utc = dt.replace(tzinfo=timezone.utc)
local = timezone.make_naive(utc, tz)
return local

View File

@ -53,9 +53,10 @@ from seahub.utils import render_permission_error, render_error, list_to_string,
gen_file_get_url, string2list, MAX_INT, IS_EMAIL_CONFIGURED, \ gen_file_get_url, string2list, MAX_INT, IS_EMAIL_CONFIGURED, \
EVENTS_ENABLED, get_user_events, get_org_user_events, show_delete_days, \ EVENTS_ENABLED, get_user_events, get_org_user_events, show_delete_days, \
TRAFFIC_STATS_ENABLED, get_user_traffic_stat, new_merge_with_no_conflict, \ TRAFFIC_STATS_ENABLED, get_user_traffic_stat, new_merge_with_no_conflict, \
user_traffic_over_limit user_traffic_over_limit, send_perm_audit_msg, get_origin_repo_info
from seahub.utils.paginator import get_page_range from seahub.utils.paginator import get_page_range
from seahub.utils.star import get_dir_starred_files from seahub.utils.star import get_dir_starred_files
from seahub.utils.timeutils import utc_to_local
from seahub.views.modules import MOD_PERSONAL_WIKI, enable_mod_for_user, \ from seahub.views.modules import MOD_PERSONAL_WIKI, enable_mod_for_user, \
disable_mod_for_user disable_mod_for_user
from seahub.utils.devices import get_user_devices, do_unlink_device from seahub.utils.devices import get_user_devices, do_unlink_device
@ -63,7 +64,8 @@ import seahub.settings as settings
from seahub.settings import FILE_PREVIEW_MAX_SIZE, INIT_PASSWD, USE_PDFJS, \ from seahub.settings import FILE_PREVIEW_MAX_SIZE, INIT_PASSWD, USE_PDFJS, \
FILE_ENCODING_LIST, FILE_ENCODING_TRY_LIST, AVATAR_FILE_STORAGE, \ FILE_ENCODING_LIST, FILE_ENCODING_TRY_LIST, AVATAR_FILE_STORAGE, \
SEND_EMAIL_ON_ADDING_SYSTEM_MEMBER, SEND_EMAIL_ON_RESETTING_USER_PASSWD, \ SEND_EMAIL_ON_ADDING_SYSTEM_MEMBER, SEND_EMAIL_ON_RESETTING_USER_PASSWD, \
ENABLE_SUB_LIBRARY, ENABLE_REPO_HISTORY_SETTING, REPO_PASSWORD_MIN_LENGTH ENABLE_SUB_LIBRARY, ENABLE_REPO_HISTORY_SETTING, \
REPO_PASSWORD_MIN_LENGTH, ENABLE_FOLDER_PERM
# Get an instance of a logger # Get an instance of a logger
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -477,6 +479,7 @@ def repo_basic_info(request, repo_id):
'no_history_enabled': no_history_enabled, 'no_history_enabled': no_history_enabled,
'partial_history_enabled': partial_history_enabled, 'partial_history_enabled': partial_history_enabled,
'days_enabled': days_enabled, 'days_enabled': days_enabled,
'ENABLE_FOLDER_PERM': ENABLE_FOLDER_PERM,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
@login_required @login_required
@ -491,6 +494,7 @@ def repo_transfer_owner(request, repo_id):
return render_to_response('repo_transfer_owner.html', { return render_to_response('repo_transfer_owner.html', {
'repo': repo, 'repo': repo,
'ENABLE_FOLDER_PERM': ENABLE_FOLDER_PERM,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
@login_required @login_required
@ -506,6 +510,7 @@ def repo_change_password(request, repo_id):
return render_to_response('repo_change_password.html', { return render_to_response('repo_change_password.html', {
'repo': repo, 'repo': repo,
'repo_password_min_length': REPO_PASSWORD_MIN_LENGTH, 'repo_password_min_length': REPO_PASSWORD_MIN_LENGTH,
'ENABLE_FOLDER_PERM': ENABLE_FOLDER_PERM,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
@login_required @login_required
@ -565,6 +570,7 @@ def repo_shared_link(request, repo_id):
'repo': repo, 'repo': repo,
'fileshares': p_fileshares, 'fileshares': p_fileshares,
'uploadlinks': p_uploadlinks, 'uploadlinks': p_uploadlinks,
'ENABLE_FOLDER_PERM': ENABLE_FOLDER_PERM,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
@login_required @login_required
@ -598,6 +604,7 @@ def repo_share_manage(request, repo_id):
'repo': repo, 'repo': repo,
'repo_share_user': repo_share_user, 'repo_share_user': repo_share_user,
'repo_share_group': repo_share_group, 'repo_share_group': repo_share_group,
'ENABLE_FOLDER_PERM': ENABLE_FOLDER_PERM,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
@login_required @login_required
@ -607,11 +614,42 @@ def repo_folder_perm(request, repo_id):
username = request.user.username username = request.user.username
can_access, repo = can_access_repo_setting(request, repo_id, username) can_access, repo = can_access_repo_setting(request, repo_id, username)
if not can_access: if not can_access or not ENABLE_FOLDER_PERM:
raise Http404 raise Http404
def not_need_delete(perm):
repo_id = perm.repo_id
path = perm.path
group_id = perm.group_id if hasattr(perm, 'group_id') else None
email = perm.user if hasattr(perm, 'user') else None
repo = get_repo(repo_id)
dir_id = seafile_api.get_dir_id_by_path(repo_id, path)
if group_id is not None:
# is a group folder perm
group = get_group(group_id)
if repo is None or dir_id is None or group is None:
seafile_api.rm_folder_group_perm(repo_id, path, group_id)
return False
if email is not None:
# is a user folder perm
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
user = None
if repo is None or dir_id is None or user is None:
seafile_api.rm_folder_user_perm(repo_id, path, email)
return False
return True
# for user folder permission # for user folder permission
user_folder_perms = seafile_api.list_folder_user_perm_by_repo(repo_id) user_folder_perms = seafile_api.list_folder_user_perm_by_repo(repo_id)
user_folder_perms = filter(lambda x: not_need_delete(x), user_folder_perms)
user_folder_perms.reverse() user_folder_perms.reverse()
for folder_perm in user_folder_perms: for folder_perm in user_folder_perms:
@ -624,6 +662,8 @@ def repo_folder_perm(request, repo_id):
# for group folder permission # for group folder permission
group_folder_perms = seafile_api.list_folder_group_perm_by_repo(repo_id) group_folder_perms = seafile_api.list_folder_group_perm_by_repo(repo_id)
group_folder_perms = filter(lambda x: not_need_delete(x), group_folder_perms)
group_folder_perms.reverse() group_folder_perms.reverse()
for folder_perm in group_folder_perms: for folder_perm in group_folder_perms:
@ -633,15 +673,26 @@ def repo_folder_perm(request, repo_id):
folder_perm.folder_name = _(u'Root Directory') folder_perm.folder_name = _(u'Root Directory')
else: else:
folder_perm.folder_name = os.path.basename(folder_path) folder_perm.folder_name = os.path.basename(folder_path)
folder_perm.group_name = get_group(folder_perm.group_id).group_name folder_perm.group_name = get_group(folder_perm.group_id).group_name
# contacts that already registered
sys_contacts = []
contacts = Contact.objects.get_contacts_by_user(username) contacts = Contact.objects.get_contacts_by_user(username)
for contact in contacts:
try:
user = User.objects.get(email = contact.contact_email)
except User.DoesNotExist:
user = None
if user is not None:
sys_contacts.append(contact.contact_email)
return render_to_response('repo_folder_perm.html', { return render_to_response('repo_folder_perm.html', {
'repo': repo, 'repo': repo,
'user_folder_perms': user_folder_perms, 'user_folder_perms': user_folder_perms,
'group_folder_perms': group_folder_perms, 'group_folder_perms': group_folder_perms,
'contacts': contacts, 'contacts': sys_contacts,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
def upload_error_msg (code): def upload_error_msg (code):
@ -1098,6 +1149,9 @@ def unsetinnerpub(request, repo_id):
Only system admin, organization admin or repo owner can perform this op. Only system admin, organization admin or repo owner can perform this op.
""" """
repo = get_repo(repo_id) repo = get_repo(repo_id)
perm = request.GET.get('permission', None)
if perm is None:
return render_error(request, _(u'Argument is not valid'))
if not repo: if not repo:
messages.error(request, _('Failed to unshare the library, as it does not exist.')) messages.error(request, _('Failed to unshare the library, as it does not exist.'))
return HttpResponseRedirect(reverse('share_admin')) return HttpResponseRedirect(reverse('share_admin'))
@ -1124,6 +1178,17 @@ def unsetinnerpub(request, repo_id):
else: else:
seaserv.unset_inner_pub_repo(repo.id) seaserv.unset_inner_pub_repo(repo.id)
origin_repo_id, origin_path = get_origin_repo_info(repo.id)
if origin_repo_id is not None:
perm_repo_id = origin_repo_id
perm_path = origin_path
else:
perm_repo_id = repo.id
perm_path = '/'
send_perm_audit_msg('delete-repo-perm', username, 'all', \
perm_repo_id, perm_path, perm)
messages.success(request, _('Unshare "%s" successfully.') % repo.name) messages.success(request, _('Unshare "%s" successfully.') % repo.name)
except SearpcError: except SearpcError:
messages.error(request, _('Failed to unshare "%s".') % repo.name) messages.error(request, _('Failed to unshare "%s".') % repo.name)
@ -1793,14 +1858,6 @@ def group_events_data(events):
""" """
Group events according to the date. Group events according to the date.
""" """
# e.timestamp is a datetime.datetime in UTC
# change from UTC timezone to current seahub timezone
def utc_to_local(dt):
tz = timezone.get_default_timezone()
utc = dt.replace(tzinfo=timezone.utc)
local = timezone.make_naive(utc, tz)
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)

View File

@ -45,7 +45,7 @@ from seahub.utils import check_filename_with_rename, EMPTY_SHA1, \
new_merge_with_no_conflict, get_commit_before_new_merge, \ new_merge_with_no_conflict, get_commit_before_new_merge, \
get_repo_last_modify, gen_file_upload_url, is_org_context, \ get_repo_last_modify, gen_file_upload_url, is_org_context, \
get_org_user_events, get_user_events, get_file_type_and_ext, \ get_org_user_events, get_user_events, get_file_type_and_ext, \
is_valid_username is_valid_username, send_perm_audit_msg
from seahub.utils.repo import check_group_folder_perm_args, \ from seahub.utils.repo import check_group_folder_perm_args, \
check_user_folder_perm_args check_user_folder_perm_args
from seahub.utils.star import star_file, unstar_file from seahub.utils.star import star_file, unstar_file
@ -1715,7 +1715,10 @@ def repo_history_changes(request, repo_id):
# 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:
return HttpResponse(json.dumps(changes), content_type=content_type) if request.user.is_staff is True:
pass # Allow system staff to check repo changes
else:
return HttpResponse(json.dumps(changes), content_type=content_type)
username = request.user.username username = request.user.username
try: try:
@ -2109,6 +2112,8 @@ def add_user_folder_permission(request, repo_id):
try: try:
seafile_api.add_folder_user_perm(repo_id, path, perm, user) seafile_api.add_folder_user_perm(repo_id, path, perm, user)
send_perm_audit_msg('add-repo-perm', request.user.username, user, \
repo_id, path, perm)
except SearpcError, e: except SearpcError, e:
return HttpResponse(json.dumps({"error": e.msg}), status=500, return HttpResponse(json.dumps({"error": e.msg}), status=500,
content_type=content_type) content_type=content_type)
@ -2127,8 +2132,9 @@ def remove_user_folder_permission(request, repo_id):
user = request.GET.get('user', None) user = request.GET.get('user', None)
path = request.GET.get('path', None) path = request.GET.get('path', None)
perm = request.GET.get('perm', None)
if not user or not path: if not user or not path or not perm:
return HttpResponse(json.dumps({"error": _('Argument missing')}), return HttpResponse(json.dumps({"error": _('Argument missing')}),
status=400, status=400,
content_type=content_type) content_type=content_type)
@ -2140,6 +2146,8 @@ def remove_user_folder_permission(request, repo_id):
try: try:
seafile_api.rm_folder_user_perm(repo_id, path, user) seafile_api.rm_folder_user_perm(repo_id, path, user)
send_perm_audit_msg('delete-repo-perm', request.user.username, user, \
repo_id, path, perm)
return HttpResponse(json.dumps({'success': True}), return HttpResponse(json.dumps({'success': True}),
content_type=content_type) content_type=content_type)
except SearpcError, e: except SearpcError, e:
@ -2161,7 +2169,6 @@ def toggle_user_folder_permission(request, repo_id):
path = request.POST.get('path', None) path = request.POST.get('path', None)
perm = request.POST.get('perm', None) perm = request.POST.get('perm', None)
print user, path, perm
if not user or not path or not perm: if not user or not path or not perm:
return HttpResponse(json.dumps({"error": _('Argument missing')}), return HttpResponse(json.dumps({"error": _('Argument missing')}),
status=400, status=400,
@ -2174,6 +2181,8 @@ def toggle_user_folder_permission(request, repo_id):
try: try:
seafile_api.set_folder_user_perm(repo_id, path, perm, user) seafile_api.set_folder_user_perm(repo_id, path, perm, user)
send_perm_audit_msg('modify-repo-perm', request.user.username, user, \
repo_id, path, perm)
return HttpResponse(json.dumps({'success': True}), return HttpResponse(json.dumps({'success': True}),
content_type=content_type) content_type=content_type)
except SearpcError, e: except SearpcError, e:
@ -2207,6 +2216,8 @@ def add_group_folder_permission(request, repo_id):
try: try:
seafile_api.add_folder_group_perm(repo_id, path, perm, group_id) seafile_api.add_folder_group_perm(repo_id, path, perm, group_id)
send_perm_audit_msg('add-repo-perm', request.user.username, \
group_id, repo_id, path, perm)
except SearpcError, e: except SearpcError, e:
return HttpResponse(json.dumps({"error": e.msg}), status=500, return HttpResponse(json.dumps({"error": e.msg}), status=500,
content_type=content_type) content_type=content_type)
@ -2225,8 +2236,9 @@ def remove_group_folder_permission(request, repo_id):
group_id = int(request.GET.get('group_id', None)) group_id = int(request.GET.get('group_id', None))
path = request.GET.get('path', None) path = request.GET.get('path', None)
perm = request.GET.get('perm', None)
if not group_id or not path: if not group_id or not path or not perm:
return HttpResponse(json.dumps({"error": _('Argument missing')}), return HttpResponse(json.dumps({"error": _('Argument missing')}),
status=400, status=400,
content_type=content_type) content_type=content_type)
@ -2238,6 +2250,8 @@ def remove_group_folder_permission(request, repo_id):
try: try:
seafile_api.rm_folder_group_perm(repo_id, path, group_id) seafile_api.rm_folder_group_perm(repo_id, path, group_id)
send_perm_audit_msg('delete-repo-perm', request.user.username, \
group_id, repo_id, path, perm)
return HttpResponse(json.dumps({'success': True}), return HttpResponse(json.dumps({'success': True}),
content_type=content_type) content_type=content_type)
except SearpcError, e: except SearpcError, e:
@ -2271,6 +2285,8 @@ def toggle_group_folder_permission(request, repo_id):
try: try:
seafile_api.set_folder_group_perm(repo_id, path, perm, group_id) seafile_api.set_folder_group_perm(repo_id, path, perm, group_id)
send_perm_audit_msg('modify-repo-perm', request.user.username, \
group_id, repo_id, path, perm)
return HttpResponse(json.dumps({'success': True}), return HttpResponse(json.dumps({'success': True}),
content_type=content_type) content_type=content_type)
except SearpcError, e: except SearpcError, e:

View File

@ -1079,8 +1079,8 @@ def send_file_download_msg(request, repo, path, dl_type):
ip = get_remote_ip(request) ip = get_remote_ip(request)
user_agent = request.META.get("HTTP_USER_AGENT") user_agent = request.META.get("HTTP_USER_AGENT")
msg = 'file-download-%s\t%s\t%s\t%s\t%s\t%s\t%s' % \ msg = 'file-download-%s\t%s\t%s\t%s\t%s\t%s' % \
(dl_type, username, ip, user_agent, repo.id, repo.name, path) (dl_type, username, ip, user_agent, repo.id, path)
msg_utf8 = msg.encode('utf-8') msg_utf8 = msg.encode('utf-8')
try: try:
@ -1137,6 +1137,7 @@ def download_file(request, repo_id, obj_id):
# send stats message # send stats message
if from_shared_link: if from_shared_link:
send_file_download_msg(request, repo, path, 'share-link')
try: try:
file_size = seafile_api.get_file_size(repo.store_id, repo.version, file_size = seafile_api.get_file_size(repo.store_id, repo.version,
obj_id) obj_id)