diff --git a/seahub/api2/endpoints/admin/groups.py b/seahub/api2/endpoints/admin/groups.py index a8f6f22381..06271fbc38 100644 --- a/seahub/api2/endpoints/admin/groups.py +++ b/seahub/api2/endpoints/admin/groups.py @@ -383,10 +383,6 @@ class AdminDepartments(APIView): result = [] for group in all_groups: - try: - avatar_url, is_default, date_uploaded = api_grp_avatar_url(group.id, avatar_size) - except: - avatar_url = get_default_group_avatar_url() created_at = timestamp_to_isoformat_timestr(group.timestamp) department_info = { "id": group.id, @@ -395,7 +391,6 @@ class AdminDepartments(APIView): "name": group.group_name, "owner": group.creator_name, "created_at": created_at, - "avatar_url": request.build_absolute_uri(avatar_url), } result.append(department_info) diff --git a/seahub/api2/endpoints/departments.py b/seahub/api2/endpoints/departments.py index e0159b4ca7..12912c55b8 100644 --- a/seahub/api2/endpoints/departments.py +++ b/seahub/api2/endpoints/departments.py @@ -66,12 +66,6 @@ class Departments(APIView): if not is_group_member(department.id, username): continue - try: - avatar_url, is_default, date_uploaded = api_grp_avatar_url(department.id, avatar_size) - except Exception as e: - logger.error(e) - avatar_url = get_default_group_avatar_url() - created_at = timestamp_to_isoformat_timestr(department.timestamp) department_info = { @@ -81,7 +75,6 @@ class Departments(APIView): "name": department.group_name, "owner": department.creator_name, "created_at": created_at, - "avatar_url": request.build_absolute_uri(avatar_url), } result.append(department_info) diff --git a/seahub/api2/endpoints/groups.py b/seahub/api2/endpoints/groups.py index 7842f67e53..9a2f81c594 100644 --- a/seahub/api2/endpoints/groups.py +++ b/seahub/api2/endpoints/groups.py @@ -18,8 +18,6 @@ from seahub.api2.authentication import TokenAuthentication from seahub.api2.throttling import UserRateThrottle from seahub.api2.endpoints.group_owned_libraries import get_group_id_by_repo_owner from seahub.avatar.settings import GROUP_AVATAR_DEFAULT_SIZE -from seahub.avatar.templatetags.group_avatar_tags import api_grp_avatar_url, \ - get_default_group_avatar_url from seahub.utils import is_org_context, is_valid_username, is_pro_version from seahub.utils.repo import get_repo_owner from seahub.utils.timeutils import timestamp_to_isoformat_timestr @@ -50,12 +48,6 @@ def get_group_admins(group_id): def get_group_info(request, group_id, avatar_size=GROUP_AVATAR_DEFAULT_SIZE): group = ccnet_api.get_group(group_id) - try: - avatar_url, is_default, date_uploaded = api_grp_avatar_url(group.id, avatar_size) - except Exception as e: - logger.error(e) - avatar_url = get_default_group_avatar_url() - isoformat_timestr = timestamp_to_isoformat_timestr(group.timestamp) group_info = { "id": group.id, @@ -63,7 +55,6 @@ def get_group_info(request, group_id, avatar_size=GROUP_AVATAR_DEFAULT_SIZE): "name": group.group_name, "owner": group.creator_name, "created_at": isoformat_timestr, - "avatar_url": request.build_absolute_uri(avatar_url), "admins": get_group_admins(group.id), } # parent_group_id = 0: non department group diff --git a/seahub/api2/urls.py b/seahub/api2/urls.py index 51c118490d..e4326b4460 100644 --- a/seahub/api2/urls.py +++ b/seahub/api2/urls.py @@ -88,8 +88,6 @@ urlpatterns = [ path('unseen_messages/', UnseenMessagesCountView.as_view()), re_path(r'^avatars/user/(?P\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/resized/(?P[0-9]+)/$', UserAvatarView.as_view()), - path('avatars/group//resized//', GroupAvatarView.as_view()), - path('groups/', Groups.as_view()), path('groups//', Groups.as_view()), path('groups//members/', GroupMembers.as_view()), diff --git a/seahub/api2/views.py b/seahub/api2/views.py index 00e811fcf1..a75d758bf3 100644 --- a/seahub/api2/views.py +++ b/seahub/api2/views.py @@ -5103,19 +5103,6 @@ class UserAvatarView(APIView): "mtime": get_timestamp(date_uploaded) } return Response(ret) -class GroupAvatarView(APIView): - authentication_classes = (TokenAuthentication, ) - permission_classes = (IsAuthenticated,) - throttle_classes = (UserRateThrottle, ) - - def get(self, request, group_id, size, format=None): - url, is_default, date_uploaded = api_grp_avatar_url(group_id, int(size)) - ret = { - "url": request.build_absolute_uri(url), - "is_default": is_default, - "mtime": get_timestamp(date_uploaded)} - return Response(ret) - class RepoHistoryChange(APIView): authentication_classes = (TokenAuthentication, ) permission_classes = (IsAuthenticated,) diff --git a/seahub/dingtalk/views.py b/seahub/dingtalk/views.py index 9d4633375b..c96601ec9e 100644 --- a/seahub/dingtalk/views.py +++ b/seahub/dingtalk/views.py @@ -12,6 +12,7 @@ from hashlib import sha256 from django.http import HttpResponseRedirect from django.urls import reverse +from django.utils.http import url_has_allowed_host_and_scheme from django.utils.translation import gettext as _ from seahub.api2.utils import get_api_token @@ -264,8 +265,11 @@ def dingtalk_disconnect(request): Profile.objects.filter(user=username).delete() SocialAuthUser.objects.delete_by_username_and_provider(username, 'dingtalk') + redirect_to = request.GET.get(auth.REDIRECT_FIELD_NAME, '/') + if not url_has_allowed_host_and_scheme(url=redirect_to, allowed_hosts=request.get_host()): + redirect_to = '/' - return HttpResponseRedirect(request.GET.get(auth.REDIRECT_FIELD_NAME, '/')) + return HttpResponseRedirect(redirect_to) # for 10.0 or later @@ -411,7 +415,11 @@ def dingtalk_connect_new(request): state = str(uuid.uuid4()) request.session['dingtalk_connect_state'] = state - request.session['dingtalk_connect_redirect'] = request.GET.get(auth.REDIRECT_FIELD_NAME, '/') + redirect_to = request.GET.get(auth.REDIRECT_FIELD_NAME, '/') + if not url_has_allowed_host_and_scheme(url=redirect_to, allowed_hosts=request.get_host()): + redirect_to = '/' + request.session['dingtalk_connect_redirect'] = redirect_to + data = { 'redirect_uri': get_site_scheme_and_netloc() + reverse('dingtalk_connect_callback'), diff --git a/seahub/group/views.py b/seahub/group/views.py index 25826ca721..762c76213e 100644 --- a/seahub/group/views.py +++ b/seahub/group/views.py @@ -8,6 +8,8 @@ from django.http import HttpResponseRedirect from django.shortcuts import render from urllib.parse import quote + +from django.utils.http import url_has_allowed_host_and_scheme from django.utils.translation import gettext as _ from seahub.auth.decorators import login_required @@ -121,6 +123,8 @@ def group_remove(request, group_id): """ # Request header may missing HTTP_REFERER, we need to handle that case. next_page = request.headers.get('referer', SITE_ROOT) + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = SITE_ROOT try: group_id_int = int(group_id) diff --git a/seahub/institutions/views.py b/seahub/institutions/views.py index 758e1f3978..6005441650 100644 --- a/seahub/institutions/views.py +++ b/seahub/institutions/views.py @@ -6,6 +6,7 @@ from django.urls import reverse from django.contrib import messages from django.http import HttpResponseRedirect, HttpResponse from django.shortcuts import render +from django.utils.http import url_has_allowed_host_and_scheme from django.utils.translation import gettext as _ import seaserv @@ -212,6 +213,8 @@ def user_remove(request, email): """ referer = request.headers.get('referer', None) next_page = reverse('institutions:useradmin') if referer is None else referer + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = reverse('institutions:useradmin') try: user = User.objects.get(email=email) diff --git a/seahub/options/views.py b/seahub/options/views.py index 4835056aff..9359467f65 100644 --- a/seahub/options/views.py +++ b/seahub/options/views.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- from django.http import HttpResponse, HttpResponseBadRequest, \ HttpResponseRedirect, Http404 +from django.utils.http import url_has_allowed_host_and_scheme from django.views.decorators.http import require_POST from django.contrib import messages from django.utils.translation import gettext as _ @@ -50,5 +51,8 @@ def sub_lib_enable_set(request): next_page = request.headers.get('referer', None) if next_page is None: next_page = SITE_ROOT + + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = SITE_ROOT return HttpResponseRedirect(next_page) diff --git a/seahub/organizations/api/admin/groups.py b/seahub/organizations/api/admin/groups.py index e076e2a1a1..89ec38cc91 100644 --- a/seahub/organizations/api/admin/groups.py +++ b/seahub/organizations/api/admin/groups.py @@ -232,10 +232,6 @@ class OrgAdminDepartments(APIView): avatar_size = GROUP_AVATAR_DEFAULT_SIZE result = [] for group in departments: - try: - avatar_url, is_default, date_uploaded = api_grp_avatar_url(group.id, avatar_size) - except: - avatar_url = get_default_group_avatar_url() created_at = timestamp_to_isoformat_timestr(group.timestamp) department_info = { "id": group.id, @@ -244,7 +240,6 @@ class OrgAdminDepartments(APIView): "name": group.group_name, "owner": group.creator_name, "created_at": created_at, - "avatar_url": request.build_absolute_uri(avatar_url), } result.append(department_info) diff --git a/seahub/profile/views.py b/seahub/profile/views.py index 91b01d4a09..e2180cc0ff 100644 --- a/seahub/profile/views.py +++ b/seahub/profile/views.py @@ -6,6 +6,7 @@ from django.urls import reverse from django.http import HttpResponse, HttpResponseRedirect, Http404 from django.shortcuts import render from django.contrib import messages +from django.utils.http import url_has_allowed_host_and_scheme from django.utils.translation import gettext as _ import seaserv @@ -271,6 +272,8 @@ def delete_user_account(request): if not ENABLE_DELETE_ACCOUNT or is_org_context(request): messages.error(request, _('Permission denied.')) next_page = request.headers.get('referer', settings.SITE_ROOT) + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = settings.SITE_ROOT return HttpResponseRedirect(next_page) if request.method != 'POST': @@ -281,6 +284,8 @@ def delete_user_account(request): if username == 'demo@seafile.com': messages.error(request, _('Demo account can not be deleted.')) next_page = request.headers.get('referer', settings.SITE_ROOT) + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = settings.SITE_ROOT return HttpResponseRedirect(next_page) user = User.objects.get(email=username) @@ -303,6 +308,8 @@ def default_repo(request): repo_id = request.POST.get('dst_repo', '') referer = request.headers.get('referer', None) next_page = settings.SITE_ROOT if referer is None else referer + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = settings.SITE_ROOT repo = seafile_api.get_repo(repo_id) if repo is None: diff --git a/seahub/views/__init__.py b/seahub/views/__init__.py index 05228043eb..f3b28a37f4 100644 --- a/seahub/views/__init__.py +++ b/seahub/views/__init__.py @@ -16,6 +16,7 @@ from django.contrib import messages from django.utils.html import escape from django.urls import reverse, resolve from django.shortcuts import render, redirect +from django.utils.http import url_has_allowed_host_and_scheme from django.utils.translation import gettext as _ from django.views.decorators.http import condition from django.http import HttpResponse, Http404, \ @@ -446,6 +447,9 @@ def repo_revert_history(request, repo_id): if not next_page: next_page = settings.SITE_ROOT + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = settings.SITE_ROOT + repo = get_repo(repo_id) if not repo: messages.error(request, _("Library does not exist")) @@ -764,6 +768,9 @@ def i18n(request): """ from django.conf import settings next_page = request.headers.get('referer', settings.SITE_ROOT) + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = settings.SITE_ROOT + lang = request.GET.get('lang', settings.LANGUAGE_CODE) if lang not in [e[0] for e in settings.LANGUAGES]: @@ -1012,8 +1019,13 @@ def client_token_login(request): else: request.client_token_login = True auth_login(request, user) + + + next_page = request.GET.get("next", reverse('libraries')) + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = reverse('libraries') - return HttpResponseRedirect(request.GET.get("next", reverse('libraries'))) + return HttpResponseRedirect(next_page) def choose_register(request): """ diff --git a/seahub/views/sysadmin.py b/seahub/views/sysadmin.py index ea17c00e4f..748a3f9c27 100644 --- a/seahub/views/sysadmin.py +++ b/seahub/views/sysadmin.py @@ -13,6 +13,7 @@ from django.urls import reverse from django.contrib import messages from django.http import HttpResponse, Http404, HttpResponseRedirect, HttpResponseNotAllowed from django.shortcuts import render +from django.utils.http import url_has_allowed_host_and_scheme from django.utils.translation import gettext as _ from seaserv import ccnet_threaded_rpc, seafserv_threaded_rpc, \ @@ -154,6 +155,9 @@ def sys_useradmin_export_excel(request): next_page = request.headers.get('referer', None) if not next_page: next_page = SITE_ROOT + + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = SITE_ROOT try: users = ccnet_api.get_emailusers('DB', -1, -1) + \ @@ -342,6 +346,8 @@ def user_remove(request, email): """Remove user""" referer = request.headers.get('referer', None) next_page = reverse('sys_info') if referer is None else referer + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = reverse('sys_info') try: user = User.objects.get(email=email) @@ -399,6 +405,8 @@ def user_remove_admin(request, email): referer = request.headers.get('referer', None) next_page = reverse('sys_info') if referer is None else referer + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = reverse('sys_info') return HttpResponseRedirect(next_page) @@ -528,6 +536,8 @@ def user_reset(request, email): referer = request.headers.get('referer', None) next_page = reverse('sys_info') if referer is None else referer + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = reverse('sys_info') return HttpResponseRedirect(next_page) @@ -632,6 +642,9 @@ def sys_group_admin_export_excel(request): next_page = request.headers.get('referer', None) if not next_page: next_page = SITE_ROOT + + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = SITE_ROOT try: groups = ccnet_threaded_rpc.get_all_groups(-1, -1) @@ -691,6 +704,9 @@ def sys_repo_delete(request, repo_id): next_page = request.headers.get('referer', None) if not next_page: next_page = HASH_URLS['SYS_REPO_ADMIN'] + + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = HASH_URLS['SYS_REPO_ADMIN'] if get_system_default_repo_id() == repo_id: messages.error(request, _('System library can not be deleted.')) @@ -760,6 +776,10 @@ def batch_add_user_example(request): next_page = request.headers.get('referer', None) if not next_page: next_page = SITE_ROOT + + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = SITE_ROOT + data_list = [] if not is_org_context(request): head = [_('Email'), @@ -808,6 +828,9 @@ def sys_sudo_mode(request): raise Http404 next_page = request.GET.get('next', reverse('sys_info')) + if not url_has_allowed_host_and_scheme(url=next_page, allowed_hosts=request.get_host()): + next_page = reverse('sys_info') + password_error = False if request.method == 'POST': password = request.POST.get('password') diff --git a/seahub/weixin/views.py b/seahub/weixin/views.py index d26b6f3036..f65f912c54 100644 --- a/seahub/weixin/views.py +++ b/seahub/weixin/views.py @@ -8,6 +8,7 @@ import requests from django.http import HttpResponseRedirect from django.urls import reverse from django.core.files.base import ContentFile +from django.utils.http import url_has_allowed_host_and_scheme from django.utils.translation import gettext as _ from django.shortcuts import render @@ -42,6 +43,8 @@ def weixin_oauth_login(request): state = str(uuid.uuid4()) request.session['weixin_oauth_login_state'] = state redirect_to = request.GET.get(auth.REDIRECT_FIELD_NAME, SITE_ROOT) + if not url_has_allowed_host_and_scheme(url=redirect_to, allowed_hosts=request.get_host()): + redirect_to = SITE_ROOT request.session['weixin_oauth_login_redirect'] = redirect_to redirect_uri = get_site_scheme_and_netloc() + reverse('weixin_oauth_callback') @@ -164,6 +167,7 @@ def weixin_oauth_connect(request): state = str(uuid.uuid4()) request.session['weixin_oauth_connect_state'] = state redirect_to = request.GET.get(auth.REDIRECT_FIELD_NAME, SITE_ROOT) + request.session['weixin_oauth_connect_redirect'] = redirect_to redirect_uri = get_site_scheme_and_netloc() + reverse('weixin_oauth_connect_callback') @@ -235,5 +239,10 @@ def weixin_oauth_disconnect(request): SocialAuthUser.objects.delete_by_username_and_provider(username, 'weixin') # redirect user to page - response = HttpResponseRedirect(request.GET.get(auth.REDIRECT_FIELD_NAME, SITE_ROOT)) + redirect_to = request.GET.get(auth.REDIRECT_FIELD_NAME, SITE_ROOT) + if not url_has_allowed_host_and_scheme(url=redirect_to, allowed_hosts=request.get_host()): + redirect_to = SITE_ROOT + + response = HttpResponseRedirect(redirect_to) + return response diff --git a/tests/api/endpoints/test_groups.py b/tests/api/endpoints/test_groups.py index 4c633eb6d6..18e8f5e898 100644 --- a/tests/api/endpoints/test_groups.py +++ b/tests/api/endpoints/test_groups.py @@ -34,7 +34,7 @@ class GroupsTest(BaseTestCase): self.assertEqual(200, resp.status_code) json_resp = json.loads(resp.content) - assert len(json_resp[0]) == 8 + assert len(json_resp[0]) == 7 group_ids = [] for group in json_resp: @@ -47,7 +47,7 @@ class GroupsTest(BaseTestCase): self.assertEqual(200, resp.status_code) json_resp = json.loads(resp.content) - assert len(json_resp[0]) == 9 + assert len(json_resp[0]) == 8 group_ids = [] group_repos = [] @@ -70,7 +70,7 @@ class GroupsTest(BaseTestCase): self.assertEqual(201, resp.status_code) json_resp = json.loads(resp.content) - assert len(json_resp) == 8 + assert len(json_resp) == 7 assert json_resp['name'] == new_group_name assert json_resp['owner'] == self.user.email @@ -82,7 +82,7 @@ class GroupsTest(BaseTestCase): self.assertEqual(201, resp.status_code) json_resp = json.loads(resp.content) - assert len(json_resp) == 8 + assert len(json_resp) == 7 assert json_resp['name'] == new_group_name assert json_resp['owner'] == self.user.email diff --git a/tests/api/test_avatar.py b/tests/api/test_avatar.py index 4cda2996e5..f0bebc2a19 100644 --- a/tests/api/test_avatar.py +++ b/tests/api/test_avatar.py @@ -11,15 +11,3 @@ class AvatarApiTest(ApiTestBase): self.assertIsNotNone(info['url']) self.assertIsNotNone(info['is_default']) self.assertIsNotNone(info['mtime']) - - def test_group_avatar(self): - gname = randstring(16) - data = {'group_name': gname} - res = self.put(GROUPS_URL, data=data) - gid = res.json()['group_id'] - avatar_url = urljoin(AVATAR_BASE_URL, 'group', str(gid), '/resized/80/') - info = self.get(avatar_url).json() - self.assertIsNotNone(info) - self.assertIsNotNone(info['url']) - self.assertIsNotNone(info['is_default']) - self.assertIsNotNone(info['mtime'])