mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-31 06:34:40 +00:00
@@ -1,7 +1,6 @@
|
|||||||
# Copyright (c) 2012-2016 Seafile Ltd.
|
# Copyright (c) 2012-2016 Seafile Ltd.
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
@@ -18,12 +17,13 @@ from seaserv import ccnet_api
|
|||||||
from seahub.api2.authentication import TokenAuthentication
|
from seahub.api2.authentication import TokenAuthentication
|
||||||
from seahub.api2.throttling import UserRateThrottle
|
from seahub.api2.throttling import UserRateThrottle
|
||||||
from seahub.api2.utils import api_error
|
from seahub.api2.utils import api_error
|
||||||
|
|
||||||
from seahub.utils import is_valid_email, is_org_context
|
from seahub.utils import is_valid_email, is_org_context
|
||||||
|
from seahub.utils.ccnet_db import CcnetDB
|
||||||
from seahub.base.accounts import User
|
from seahub.base.accounts import User
|
||||||
from seahub.base.templatetags.seahub_tags import email2nickname, \
|
from seahub.base.templatetags.seahub_tags import email2nickname, \
|
||||||
email2contact_email
|
email2contact_email
|
||||||
from seahub.profile.models import Profile
|
from seahub.profile.models import Profile
|
||||||
from seahub.contacts.models import Contact
|
|
||||||
from seahub.avatar.templatetags.avatar_tags import api_avatar_url
|
from seahub.avatar.templatetags.avatar_tags import api_avatar_url
|
||||||
|
|
||||||
from seahub.settings import ENABLE_GLOBAL_ADDRESSBOOK, \
|
from seahub.settings import ENABLE_GLOBAL_ADDRESSBOOK, \
|
||||||
@@ -38,12 +38,11 @@ except ImportError:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
current_path = os.path.dirname(os.path.abspath(__file__))
|
current_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
seafile_conf_dir = os.path.join(current_path, \
|
seafile_conf_dir = os.path.join(current_path, '../../../../../conf')
|
||||||
'../../../../../conf')
|
|
||||||
sys.path.append(seafile_conf_dir)
|
sys.path.append(seafile_conf_dir)
|
||||||
from seahub_custom_functions import custom_search_user
|
from seahub_custom_functions import custom_search_user
|
||||||
CUSTOM_SEARCH_USER = True
|
CUSTOM_SEARCH_USER = True
|
||||||
except ImportError as e:
|
except ImportError:
|
||||||
CUSTOM_SEARCH_USER = False
|
CUSTOM_SEARCH_USER = False
|
||||||
|
|
||||||
|
|
||||||
@@ -84,6 +83,10 @@ class SearchUser(APIView):
|
|||||||
|
|
||||||
limited_emails = []
|
limited_emails = []
|
||||||
for org_user in all_org_users:
|
for org_user in all_org_users:
|
||||||
|
|
||||||
|
if not org_user.is_active:
|
||||||
|
continue
|
||||||
|
|
||||||
# prepare limited emails for search from profile
|
# prepare limited emails for search from profile
|
||||||
limited_emails.append(org_user.email)
|
limited_emails.append(org_user.email)
|
||||||
|
|
||||||
@@ -121,18 +124,7 @@ class SearchUser(APIView):
|
|||||||
|
|
||||||
# remove duplicate emails
|
# remove duplicate emails
|
||||||
# get_emailusers_in_list can only accept 20 users at most
|
# get_emailusers_in_list can only accept 20 users at most
|
||||||
email_list = list({}.fromkeys(email_list).keys())[:20]
|
email_result = list({}.fromkeys(email_list).keys())[:20]
|
||||||
|
|
||||||
email_result = []
|
|
||||||
|
|
||||||
# remove nonexistent or inactive user
|
|
||||||
email_list_json = json.dumps(email_list)
|
|
||||||
user_obj_list = ccnet_api.get_emailusers_in_list('DB', email_list_json) + \
|
|
||||||
ccnet_api.get_emailusers_in_list('LDAP', email_list_json)
|
|
||||||
|
|
||||||
for user_obj in user_obj_list:
|
|
||||||
if user_obj.is_active:
|
|
||||||
email_result.append(user_obj.email)
|
|
||||||
|
|
||||||
# specific search `q`
|
# specific search `q`
|
||||||
user_q_obj = ccnet_api.get_emailuser(q)
|
user_q_obj = ccnet_api.get_emailuser(q)
|
||||||
@@ -194,21 +186,16 @@ def format_searched_user_result(request, users, size):
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def search_user_from_ccnet(q):
|
def search_user_from_ccnet(q, offset=0, limit=10):
|
||||||
""" Return 10 items at most.
|
""" Return 10 items at most.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
users = []
|
users = []
|
||||||
|
|
||||||
db_users = ccnet_api.search_emailusers('DB', q, 0, 10)
|
ccnet_db = CcnetDB()
|
||||||
users.extend(db_users)
|
users, count = ccnet_db.list_eligible_users(offset, limit,
|
||||||
|
is_active=True, q=q)
|
||||||
|
|
||||||
count = len(users)
|
|
||||||
if count < 10:
|
|
||||||
ldap_imported_users = ccnet_api.search_emailusers('LDAP', q, 0, 10 - count)
|
|
||||||
users.extend(ldap_imported_users)
|
|
||||||
|
|
||||||
count = len(users)
|
|
||||||
if count < 10 and ENABLE_SEARCH_FROM_LDAP_DIRECTLY:
|
if count < 10 and ENABLE_SEARCH_FROM_LDAP_DIRECTLY:
|
||||||
all_ldap_users = ccnet_api.search_ldapusers(q, 0, 10 - count)
|
all_ldap_users = ccnet_api.search_ldapusers(q, 0, 10 - count)
|
||||||
users.extend(all_ldap_users)
|
users.extend(all_ldap_users)
|
||||||
@@ -221,29 +208,44 @@ def search_user_from_ccnet(q):
|
|||||||
return email_list
|
return email_list
|
||||||
|
|
||||||
|
|
||||||
def search_user_from_profile(q):
|
def search_user_from_profile(q, offset=0, limit=10, max_attempts=10):
|
||||||
""" Return 10 items at most.
|
"""Return a list of email addresses, with a length of at least 10.
|
||||||
|
|
||||||
|
If not enough results are found, continue searching up to max_attempts times.
|
||||||
"""
|
"""
|
||||||
# 'nickname__icontains' for search by nickname
|
|
||||||
# 'contact_email__icontains' for search by contact email
|
|
||||||
users = Profile.objects.filter(Q(nickname__icontains=q) | \
|
|
||||||
Q(contact_email__icontains=q) | \
|
|
||||||
Q(login_id__icontains=q)).values('user')[:10]
|
|
||||||
|
|
||||||
email_list = []
|
email_list = []
|
||||||
for user in users:
|
attempts = 0
|
||||||
email_list.append(user['user'])
|
|
||||||
|
|
||||||
return email_list
|
ccnet_db = CcnetDB()
|
||||||
|
|
||||||
|
while len(email_list) < 10 and attempts < max_attempts:
|
||||||
|
|
||||||
|
# Search by nickname, contact email, or login ID
|
||||||
|
users = Profile.objects.filter(
|
||||||
|
Q(nickname__icontains=q) |
|
||||||
|
Q(contact_email__icontains=q) |
|
||||||
|
Q(login_id__icontains=q)
|
||||||
|
).values('user')[offset:offset + limit]
|
||||||
|
|
||||||
|
new_emails = ccnet_db.get_active_users_by_user_list([user['user'] for user in users])
|
||||||
|
email_list.extend([email for email in new_emails if email not in email_list])
|
||||||
|
offset += limit
|
||||||
|
attempts += 1
|
||||||
|
|
||||||
|
return email_list[:10]
|
||||||
|
|
||||||
|
|
||||||
def search_user_from_profile_with_limits(q, limited_emails):
|
def search_user_from_profile_with_limits(q, limited_emails):
|
||||||
""" Return 10 items at most.
|
""" Return 10 items at most.
|
||||||
"""
|
"""
|
||||||
# search within limited_emails
|
# search within limited_emails
|
||||||
users = Profile.objects.filter(Q(user__in=limited_emails) & (Q(nickname__icontains=q) | \
|
users = Profile.objects.filter(
|
||||||
Q(contact_email__icontains=q) | \
|
Q(user__in=limited_emails) & (
|
||||||
Q(login_id__icontains=q))).values('user')[:10]
|
Q(nickname__icontains=q) |
|
||||||
|
Q(contact_email__icontains=q) |
|
||||||
|
Q(login_id__icontains=q)
|
||||||
|
)
|
||||||
|
).values('user')[:10]
|
||||||
|
|
||||||
email_list = []
|
email_list = []
|
||||||
for user in users:
|
for user in users:
|
||||||
@@ -259,23 +261,6 @@ def search_user_when_global_address_book_disabled(request, q):
|
|||||||
|
|
||||||
email_list = []
|
email_list = []
|
||||||
username = request.user.username
|
username = request.user.username
|
||||||
|
|
||||||
# search from contact
|
|
||||||
# get user's contact list
|
|
||||||
contacts = Contact.objects.get_contacts_by_user(username)
|
|
||||||
for contact in contacts:
|
|
||||||
|
|
||||||
# search user from contact list
|
|
||||||
if q in contact.contact_email:
|
|
||||||
email_list.append(contact.contact_email)
|
|
||||||
|
|
||||||
# search from profile, limit search range in user's contacts
|
|
||||||
limited_emails = []
|
|
||||||
for contact in contacts:
|
|
||||||
limited_emails.append(contact.contact_email)
|
|
||||||
|
|
||||||
email_list += search_user_from_profile_with_limits(q, limited_emails)
|
|
||||||
|
|
||||||
current_user = User.objects.get(email=username)
|
current_user = User.objects.get(email=username)
|
||||||
if current_user.role.lower() != 'guest':
|
if current_user.role.lower() != 'guest':
|
||||||
|
|
||||||
|
@@ -27,12 +27,12 @@ MANAGERS = ADMINS
|
|||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
|
'ENGINE': 'django.db.backends.mysql',
|
||||||
'NAME': '%s/seahub/seahub.db' % PROJECT_ROOT, # Or path to database file if using sqlite3.
|
'NAME': 'seahub',
|
||||||
'USER': '', # Not used with sqlite3.
|
'USER': 'root',
|
||||||
'PASSWORD': '', # Not used with sqlite3.
|
'PASSWORD': 'root',
|
||||||
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
|
'HOST': '127.0.0.1',
|
||||||
'PORT': '', # Set to empty string for default. Not used with sqlite3.
|
'PORT': '3306',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import os
|
import os
|
||||||
import configparser
|
import configparser
|
||||||
|
from django.db import connection
|
||||||
|
|
||||||
|
|
||||||
def get_ccnet_db_name():
|
def get_ccnet_db_name():
|
||||||
@@ -24,11 +25,6 @@ def get_ccnet_db_name():
|
|||||||
return db_name, None
|
return db_name, None
|
||||||
|
|
||||||
|
|
||||||
import os
|
|
||||||
import configparser
|
|
||||||
from django.db import connection
|
|
||||||
|
|
||||||
|
|
||||||
class CcnetGroup(object):
|
class CcnetGroup(object):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
@@ -89,22 +85,28 @@ class CcnetDB:
|
|||||||
groups.append(group_obj)
|
groups.append(group_obj)
|
||||||
return groups
|
return groups
|
||||||
|
|
||||||
def list_eligible_users(self, start, limit, is_active=None, role=None):
|
def list_eligible_users(self, start, limit,
|
||||||
|
is_active=None, role=None, q=None):
|
||||||
|
|
||||||
def status(is_active):
|
def status(is_active):
|
||||||
return 'AND t1.is_active=%s ' % is_active
|
return f'AND t1.is_active={is_active} '
|
||||||
|
|
||||||
def is_role(role):
|
def is_role(role):
|
||||||
if role == 'default':
|
if role == 'default':
|
||||||
return 'AND (t2.role is null or t2.role = "default") '
|
return 'AND (t2.role is null or t2.role = "default") '
|
||||||
else:
|
else:
|
||||||
return 'AND t2.role = "%s"' % role
|
return f'AND t2.role = "{role}" '
|
||||||
|
|
||||||
|
def search(q):
|
||||||
|
return f'AND t1.email LIKE "%{q}%" '
|
||||||
|
|
||||||
search_clause = ''
|
search_clause = ''
|
||||||
if is_active:
|
if is_active:
|
||||||
search_clause += status(is_active)
|
search_clause += status(is_active)
|
||||||
if role:
|
if role:
|
||||||
search_clause += is_role(role)
|
search_clause += is_role(role)
|
||||||
|
if q:
|
||||||
|
search_clause += search(q)
|
||||||
|
|
||||||
count_sql = f"""
|
count_sql = f"""
|
||||||
SELECT count(1)
|
SELECT count(1)
|
||||||
@@ -130,11 +132,10 @@ class CcnetDB:
|
|||||||
WHERE
|
WHERE
|
||||||
t1.email NOT LIKE '%%@seafile_group' %s
|
t1.email NOT LIKE '%%@seafile_group' %s
|
||||||
ORDER BY t1.id
|
ORDER BY t1.id
|
||||||
LIMIT {limit} OFFSET {start}
|
LIMIT {limit} OFFSET {start};
|
||||||
""" % search_clause
|
""" % search_clause
|
||||||
|
|
||||||
users = []
|
users = []
|
||||||
total = 0
|
|
||||||
with connection.cursor() as cursor:
|
with connection.cursor() as cursor:
|
||||||
cursor.execute(count_sql)
|
cursor.execute(count_sql)
|
||||||
cursor.execute(count_sql)
|
cursor.execute(count_sql)
|
||||||
@@ -216,4 +217,5 @@ class CcnetDB:
|
|||||||
cursor.execute(sql)
|
cursor.execute(sql)
|
||||||
for user in cursor.fetchall():
|
for user in cursor.fetchall():
|
||||||
active_users.append(user[0])
|
active_users.append(user[0])
|
||||||
|
|
||||||
return active_users
|
return active_users
|
||||||
|
@@ -331,7 +331,6 @@ class SeafileDB:
|
|||||||
""" % self.db_name
|
""" % self.db_name
|
||||||
cursor.execute(del_trash_sql, (repo_ids,))
|
cursor.execute(del_trash_sql, (repo_ids,))
|
||||||
|
|
||||||
|
|
||||||
sql_list_repo_id = f"""
|
sql_list_repo_id = f"""
|
||||||
SELECT
|
SELECT
|
||||||
t.repo_id
|
t.repo_id
|
||||||
|
@@ -4,13 +4,13 @@ from mock import patch
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.test import override_settings
|
from django.test import override_settings
|
||||||
|
|
||||||
from seahub.contacts.models import Contact
|
|
||||||
from seahub.profile.models import Profile
|
from seahub.profile.models import Profile
|
||||||
from seahub.profile.utils import refresh_cache
|
from seahub.profile.utils import refresh_cache
|
||||||
from seahub.api2.endpoints.search_user import SearchUser
|
from seahub.api2.endpoints.search_user import SearchUser
|
||||||
from seahub.test_utils import BaseTestCase
|
from seahub.test_utils import BaseTestCase
|
||||||
from tests.common.utils import randstring
|
from tests.common.utils import randstring
|
||||||
|
|
||||||
|
|
||||||
class SearchUserTest(BaseTestCase):
|
class SearchUserTest(BaseTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.login_as(self.user)
|
self.login_as(self.user)
|
||||||
@@ -210,19 +210,10 @@ class SearchUserTest(BaseTestCase):
|
|||||||
resp = self.client.get(self.endpoint + '?q=%s' % contact_email)
|
resp = self.client.get(self.endpoint + '?q=%s' % contact_email)
|
||||||
json_resp = json.loads(resp.content)
|
json_resp = json.loads(resp.content)
|
||||||
self.assertEqual(200, resp.status_code)
|
self.assertEqual(200, resp.status_code)
|
||||||
assert json_resp['users'][0]['email'] == self.admin.username
|
assert json_resp['users'][0]['email'] == contact_email
|
||||||
|
|
||||||
# search with invalid email & has no contacts
|
# search with invalid email & has no contacts
|
||||||
resp = self.client.get(self.endpoint + '?q=%s' % contact_email[:6])
|
resp = self.client.get(self.endpoint + '?q=%s' % contact_email[:6])
|
||||||
json_resp = json.loads(resp.content)
|
json_resp = json.loads(resp.content)
|
||||||
self.assertEqual(200, resp.status_code)
|
self.assertEqual(200, resp.status_code)
|
||||||
assert len(json_resp['users']) == 0
|
assert len(json_resp['users']) == 0
|
||||||
|
|
||||||
# search with invalid email & has contact
|
|
||||||
nickname_of_admin = randstring(6)
|
|
||||||
Contact.objects.add_contact(self.user.username, self.admin.username)
|
|
||||||
Profile.objects.add_or_update(self.admin.username, nickname=nickname_of_admin)
|
|
||||||
resp = self.client.get(self.endpoint + '?q=%s' % nickname_of_admin)
|
|
||||||
json_resp = json.loads(resp.content)
|
|
||||||
self.assertEqual(200, resp.status_code)
|
|
||||||
assert json_resp['users'][0]['email'] == self.admin.username
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
from seahub.test_utils import BaseTestCase
|
from seahub.test_utils import BaseTestCase
|
||||||
from seahub.api2.models import TokenV2, TokenV2Manager
|
from seahub.api2.models import TokenV2
|
||||||
|
|
||||||
|
|
||||||
class TokenV2ManagerTest(BaseTestCase):
|
class TokenV2ManagerTest(BaseTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -37,7 +38,7 @@ class TokenV2ManagerTest(BaseTestCase):
|
|||||||
self.admin.username, self.token.platform, self.token.device_id,
|
self.admin.username, self.token.platform, self.token.device_id,
|
||||||
self.token.device_name, '1.1.1', '0.1.1', self.ip_v6)
|
self.token.device_name, '1.1.1', '0.1.1', self.ip_v6)
|
||||||
assert len(TokenV2.objects.all()) == 2
|
assert len(TokenV2.objects.all()) == 2
|
||||||
assert TokenV2.objects.all()[1].user == self.admin.username
|
assert self.admin.username in [item.user for item in TokenV2.objects.all()]
|
||||||
|
|
||||||
def test_delete_device_token(self):
|
def test_delete_device_token(self):
|
||||||
TokenV2.objects.delete_device_token(
|
TokenV2.objects.delete_device_token(
|
||||||
|
Reference in New Issue
Block a user