seafile-server/python/seaserv/service.py
feiniks d5fa18f120
Adjust size of max_download_dir_size and max_upload_size (#739)
* Adjust size of max_download_dir_size and max_upload_size

* Update ci python

---------

Co-authored-by: 杨赫然 <heran.yang@seafile.com>
2025-02-26 11:47:08 +08:00

942 lines
25 KiB
Python

from datetime import datetime
import json
import logging
import os
import sys
import configparser
from urllib.parse import urlparse
import seafile
import re
from pysearpc import SearpcError
_DEBUG = 'SEAFILE_DEBUG' in os.environ
ENVIRONMENT_VARIABLES = ('CCNET_CONF_DIR', 'SEAFILE_CONF_DIR')
# Used to fix bug in some rpc calls, will be removed in near future.
MAX_INT = 2147483647
def _load_path_from_env(key, check=True):
v = os.environ.get(key, '')
if not v:
if check:
raise ImportError("Seaserv cannot be imported, because environment variable %s is undefined." % key)
return None
if _DEBUG:
print("Loading %s from %s" % (key, v))
return os.path.normpath(os.path.expanduser(v))
CCNET_CONF_PATH = _load_path_from_env('CCNET_CONF_DIR')
SEAFILE_CONF_DIR = _load_path_from_env('SEAFILE_CONF_DIR')
SEAFILE_CENTRAL_CONF_DIR = _load_path_from_env('SEAFILE_CENTRAL_CONF_DIR', check=False)
SEAFILE_RPC_PIPE_PATH = _load_path_from_env ("SEAFILE_RPC_PIPE_PATH", check=False)
seafile_pipe_path = os.path.join(SEAFILE_RPC_PIPE_PATH if SEAFILE_RPC_PIPE_PATH else SEAFILE_CONF_DIR,
'seafile.sock')
seafserv_threaded_rpc = seafile.ServerThreadedRpcClient(seafile_pipe_path)
ccnet_threaded_rpc = seafserv_threaded_rpc
# load ccnet server addr and port from ccnet.conf.
# 'addr:port' is used when downloading a repo
config = configparser.ConfigParser()
config.read(os.path.join(SEAFILE_CENTRAL_CONF_DIR if SEAFILE_CENTRAL_CONF_DIR else CCNET_CONF_PATH,
'ccnet.conf'))
if config.has_option('LDAP', 'HOST'):
LDAP_HOST = config.get('LDAP', 'HOST')
else:
LDAP_HOST = None
config.read(os.path.join(SEAFILE_CENTRAL_CONF_DIR if SEAFILE_CENTRAL_CONF_DIR else SEAFILE_CONF_DIR,
'seafile.conf'))
def get_fileserver_option(key, default):
'''
"fileserver" used to be "httpserver"
'''
for section in ('fileserver', 'httpserver'):
if config.has_option(section, key):
return config.get(section, key)
return default
USE_GO_FILESERVER = False
if config.has_option('fileserver', 'use_go_fileserver'):
USE_GO_FILESERVER = config.getboolean('fileserver', 'use_go_fileserver')
MAX_UPLOAD_FILE_SIZE = None # Defaults to no limit
try:
max_upload_size_mb = int(get_fileserver_option('max_upload_size', 0))
if max_upload_size_mb > 0:
MAX_UPLOAD_FILE_SIZE = max_upload_size_mb * 1000000
except ValueError:
pass
MAX_DOWNLOAD_DIR_SIZE = 100 * 1000000 # Default max size of a downloadable dir
try:
max_download_dir_size_mb = int(get_fileserver_option('max_download_dir_size', 0))
if max_download_dir_size_mb > 0:
MAX_DOWNLOAD_DIR_SIZE = max_download_dir_size_mb * 1000000
except ValueError:
pass
FILE_SERVER_PORT = get_fileserver_option('port', '8082')
FILE_SERVER_ROOT = None
CALC_SHARE_USAGE = False
if config.has_option('quota', 'calc_share_usage'):
CALC_SHARE_USAGE = config.getboolean('quota', 'calc_share_usage')
# Get an instance of a logger
logger = logging.getLogger(__name__)
#### Basic ccnet API ####
def get_emailusers(source, start, limit, is_active=None):
if is_active is True:
status = "active" # list active users
elif is_active is False:
status = "inactive" # list inactive users
else:
status = "" # list all users
return ccnet_threaded_rpc.get_emailusers(source, start, limit, status)
def count_emailusers():
try:
ret = ccnet_threaded_rpc.count_emailusers()
except SearpcError:
ret = -1
return 0 if ret < 0 else ret
def get_emailuser_with_import(email):
return ccnet_threaded_rpc.get_emailuser_with_import(email)
# group
def get_group(group_id):
group_id_int = int(group_id)
try:
group = ccnet_threaded_rpc.get_group(group_id_int)
except SearpcError:
group = None
return group
def get_personal_groups(start, limit):
try:
groups_all = ccnet_threaded_rpc.get_all_groups(start, limit)
except SearpcError:
return []
return [ x for x in groups_all if not is_org_group(x.id) ]
def get_personal_groups_by_user(email):
try:
groups_all = ccnet_threaded_rpc.get_groups(email)
except SearpcError:
return []
return [ x for x in groups_all if not is_org_group(x.id) ]
# group user
def is_group_user(group_id, user):
try:
ret = ccnet_threaded_rpc.is_group_user(group_id, user)
except SearpcError:
ret = 0
return ret
def check_group_staff(group_id, username):
"""Check where user is group staff"""
group_id = int(group_id)
try:
ret = ccnet_threaded_rpc.check_group_staff(group_id, username)
except SearpcError as e:
logger.error(e)
ret = 0
return True if ret == 1 else False
def remove_group_user(user):
"""
Remove group user relationship.
"""
return ccnet_threaded_rpc.remove_group_user(user)
def get_group_members(group_id, start=-1, limit=-1):
group_id_int = int(group_id)
try:
members = ccnet_threaded_rpc.get_group_members(group_id_int, start, limit)
except SearpcError:
members = []
return members
# org group
def is_org_group(group_id):
try:
ret = ccnet_threaded_rpc.is_org_group(group_id)
except SearpcError:
ret = -1
return True if ret == 1 else False
def get_org_id_by_group(group_id):
try:
org_id = ccnet_threaded_rpc.get_org_id_by_group(group_id)
except SearpcError:
org_id = -1
return org_id
def get_org_groups(org_id, start, limit):
try:
groups = ccnet_threaded_rpc.get_org_groups(org_id, start, limit)
except SearpcError:
groups = []
return groups
def get_org_groups_by_user(org_id, user):
"""
Get user's groups in org.
"""
try:
groups_all = ccnet_threaded_rpc.get_groups(user)
except SearpcError:
return []
return [ x for x in groups_all if org_id == get_org_id_by_group(x.id) ]
# org
def create_org(org_name, url_prefix, username):
ccnet_threaded_rpc.create_org(org_name, url_prefix, username)
def get_org_by_url_prefix(url_prefix):
try:
org = ccnet_threaded_rpc.get_org_by_url_prefix(url_prefix)
except SearpcError:
org = None
return org
def get_org_by_id(org_id):
try:
org = ccnet_threaded_rpc.get_org_by_id(org_id)
except SearpcError:
org = None
return org
# org user
def add_org_user(org_id, email, is_staff):
try:
ccnet_threaded_rpc.add_org_user(org_id, email, is_staff)
except SearpcError:
pass
def remove_org_user(org_id, email):
try:
ccnet_threaded_rpc.remove_org_user(org_id, email)
except SearpcError:
pass
def org_user_exists(org_id, user):
try:
ret = ccnet_threaded_rpc.org_user_exists(org_id, user)
except SearpcError:
ret = -1
return True if ret == 1 else False
def get_org_users_by_url_prefix(url_prefix, start, limit):
"""
List org users.
"""
try:
users = ccnet_threaded_rpc.get_org_emailusers(url_prefix, start, limit)
except:
users = []
return users
def get_orgs_by_user(user):
try:
orgs = ccnet_threaded_rpc.get_orgs_by_user(user)
except SearpcError:
orgs = []
return orgs
def is_org_staff(org_id, user):
"""
Check whether user is staff of a org.
"""
try:
ret = ccnet_threaded_rpc.is_org_staff(org_id, user)
except SearpcError:
ret = -1
return True if ret == 1 else False
def get_user_current_org(user, url_prefix):
orgs = get_orgs_by_user(user)
for org in orgs:
if org.url_prefix == url_prefix:
return org
return None
def send_command(command):
client = pool.get_client()
client.send_cmd(command)
ret = client.response[2]
pool.return_client(client)
return ret
def send_message(msg_type, content):
client = pool.get_client()
client.send_message(msg_type, content)
pool.return_client(client)
def get_binding_peerids(email):
"""Get peer ids of a given email"""
try:
peer_ids = ccnet_threaded_rpc.get_binding_peerids(email)
except SearpcError:
return []
if not peer_ids:
return []
peerid_list = []
for peer_id in peer_ids.split("\n"):
if peer_id == '':
continue
peerid_list.append(peer_id)
return peerid_list
######## seafserv API ####
# repo
def get_repos():
"""
Return repository list.
"""
return seafserv_threaded_rpc.get_repo_list("", 100)
def get_repo(repo_id):
return seafserv_threaded_rpc.get_repo(repo_id)
def edit_repo(repo_id, name, desc, user):
try:
ret = seafserv_threaded_rpc.edit_repo(repo_id, name, desc, user)
except SearpcError as e:
ret = -1
return True if ret == 0 else False
def create_repo(name, desc, user, passwd):
"""
Return repo id if successfully created a repo, otherwise None.
"""
try:
ret = seafserv_threaded_rpc.create_repo(name, desc, user, passwd)
except SearpcError as e:
logger.error(e)
ret = None
return ret
def remove_repo(repo_id):
"""
Return true if successfully removed a repo, otherwise false.
"""
try:
ret = seafserv_threaded_rpc.remove_repo(repo_id)
except SearpcError as e:
logger.error(e)
ret = -1
return True if ret == 0 else False
def list_personal_repos_by_owner(owner):
"""
List users owned repos in personal context.
"""
try:
repos = seafserv_threaded_rpc.list_owned_repos(owner)
except SearpcError:
repos = []
return repos
def get_repo_token_nonnull(repo_id, username):
return seafserv_threaded_rpc.get_repo_token_nonnull (repo_id, username)
def get_repo_owner(repo_id):
"""
Get owner of a repo.
"""
try:
ret = seafserv_threaded_rpc.get_repo_owner(repo_id)
except SearpcError:
ret = ''
return ret
def is_repo_owner(user, repo_id):
"""
Check whether user is repo owner.
"""
try:
ret = seafserv_threaded_rpc.is_repo_owner(user, repo_id)
except SearpcError:
ret = 0
return ret
def server_repo_size(repo_id):
try:
size = seafserv_threaded_rpc.server_repo_size(repo_id)
except SearpcError:
size = 0
return size
# org repo
def create_org_repo(repo_name, repo_desc, user, passwd, org_id):
"""
Create org repo, return valid repo id if success.
"""
try:
repo_id = seafserv_threaded_rpc.create_org_repo(repo_name, repo_desc,
user, passwd, org_id)
except SearpcError:
repo_id = None
return repo_id
def is_org_repo(repo_id):
org_id = get_org_id_by_repo_id(repo_id)
return True if org_id > 0 else False
def list_org_repos_by_owner(org_id, user):
try:
repos = seafserv_threaded_rpc.list_org_repos_by_owner(org_id, user)
except SearpcError:
repos = []
return repos
def get_org_repos(org_id, start, limit):
"""
List repos created in org.
"""
try:
repos = seafserv_threaded_rpc.get_org_repo_list(org_id, start, limit)
except SearpcError:
repos = []
if repos:
for r in repos:
r.owner = get_org_repo_owner(r.id)
return repos
def get_org_id_by_repo_id(repo_id):
"""
Get org id according repo id.
"""
try:
org_id = seafserv_threaded_rpc.get_org_id_by_repo_id(repo_id)
except SearpcError:
org_id = -1
return org_id
def is_org_repo_owner(org_id, repo_id, user):
"""
Check whether user is org repo owner.
NOTE:
`org_id` may used in future.
"""
owner = get_org_repo_owner(repo_id)
if not owner:
return False
return True if owner == user else False
def get_org_repo_owner(repo_id):
"""
Get owner of org repo.
"""
try:
owner = seafserv_threaded_rpc.get_org_repo_owner(repo_id)
except SearpcError:
owner = None
return owner
# commit
def get_commit(repo_id, repo_version, cmt_id):
""" Get a commit. """
try:
ret = seafserv_threaded_rpc.get_commit(repo_id, repo_version, cmt_id)
except SearpcError:
ret = None
return ret
def get_commits(repo_id, offset, limit):
"""Get commit lists."""
try:
ret = seafserv_threaded_rpc.get_commit_list(repo_id, offset, limit)
except SearpcError:
ret = None
return ret
# branch
def get_branches(repo_id):
"""Get branches of a given repo"""
return seafserv_threaded_rpc.branch_gets(repo_id)
# group repo
def get_group_repos_by_owner(user):
"""
List user's repos that are sharing to groups
"""
try:
ret = seafserv_threaded_rpc.get_group_repos_by_owner(user)
except SearpcError:
ret = []
return ret
def get_shared_groups_by_repo(repo_id):
try:
group_ids = seafserv_threaded_rpc.get_shared_groups_by_repo(repo_id)
except SearpcError:
group_ids = ''
if not group_ids:
return []
groups = []
for group_id in group_ids.split('\n'):
if not group_id:
continue
group = get_group(group_id)
if group:
groups.append(group)
return groups
def conv_repoids_to_list(repo_ids):
"""
Convert repo ids seperated by "\n" to list.
"""
if not repo_ids:
return []
repoid_list = []
for repo_id in repo_ids.split("\n"):
if repo_id == '':
continue
repoid_list.append(repo_id)
return repoid_list
def get_group_repoids(group_id):
"""Get repo ids of a given group id."""
try:
repo_ids = seafserv_threaded_rpc.get_group_repoids(group_id)
except SearpcError:
return []
return conv_repoids_to_list(repo_ids)
def get_group_repos(group_id, user):
"""Get repos of a given group id."""
repoid_list = get_group_repoids(group_id)
repos = []
for repo_id in repoid_list:
if not repo_id:
continue
repo = get_repo(repo_id)
if not repo:
continue
repo.owner = seafserv_threaded_rpc.get_group_repo_owner(repo_id)
repo.share_from_me = True if user == repo.owner else False
last_commit = get_commits(repo.id, 0, 1)[0]
repo.latest_modify = last_commit.ctime if last_commit else None
repos.append(repo)
repos.sort(lambda x, y: cmp(y.latest_modify, x.latest_modify))
return repos
# org group repo
def del_org_group_repo(repo_id, org_id, group_id):
seafserv_threaded_rpc.del_org_group_repo(repo_id, org_id, group_id)
def get_org_group_repoids(org_id, group_id):
try:
repo_ids = seafserv_threaded_rpc.get_org_group_repoids(org_id, group_id)
except SearpcError:
repo_ids = ''
return conv_repoids_to_list(repo_ids)
def get_org_group_repos(org_id, group_id, user):
"""Get org repos of a given group id."""
repoid_list = get_org_group_repoids(org_id, group_id)
if not repoid_list:
return []
repos = []
for repo_id in repoid_list:
if not repo_id:
continue
repo = get_repo(repo_id)
if not repo:
continue
repo.owner = seafserv_threaded_rpc.get_org_group_repo_owner(org_id,
group_id,
repo_id)
repo.sharecd_from_me = True if user == repo.owner else False
last_commit = get_commits(repo.id, 0, 1)[0]
repo.latest_modify = last_commit.ctime if last_commit else None
repos.append(repo)
repos.sort(lambda x, y: cmp(y.latest_modify, x.latest_modify))
return repos
def get_org_groups_by_repo(org_id, repo_id):
try:
group_ids = seafserv_threaded_rpc.get_org_groups_by_repo(org_id,
repo_id)
except SearpcError:
group_ids = ''
if not group_ids:
return []
groups = []
for group_id in group_ids.split('\n'):
if not group_id:
continue
group = get_group(group_id)
if group:
groups.append(group)
return groups
# inner pub repo
def list_inner_pub_repos_by_owner(user):
"""
List a user's inner pub repos.
"""
try:
ret = seafserv_threaded_rpc.list_inner_pub_repos_by_owner(user)
except SearpcError:
ret = []
return ret
def list_inner_pub_repos(username):
"""
List inner pub repos, which can be access by everyone.
"""
try:
shared_repos = seafserv_threaded_rpc.list_inner_pub_repos()
except:
shared_repos = []
for repo in shared_repos:
repo.user_perm = check_permission(repo.props.repo_id, username)
shared_repos.sort(lambda x, y: cmp(y.props.last_modified, x.props.last_modified))
return shared_repos
def count_inner_pub_repos():
try:
ret = seafserv_threaded_rpc.count_inner_pub_repos()
except SearpcError:
ret = -1
return 0 if ret < 0 else ret
def is_inner_pub_repo(repo_id):
"""
Check whether a repo is public.
Return 0 if repo is not inner public, otherwise non-zero.
"""
try:
ret = seafserv_threaded_rpc.is_inner_pub_repo(repo_id)
except SearpcError:
ret = 0
return ret
def unset_inner_pub_repo(repo_id):
seafserv_threaded_rpc.unset_inner_pub_repo(repo_id)
# org inner pub repo
def list_org_inner_pub_repos(org_id, username, start=None, limit=None):
"""
List org inner pub repos, which can be access by all org members.
"""
try:
shared_repos = seafserv_threaded_rpc.list_org_inner_pub_repos(org_id)
except SearpcError:
shared_repos = []
for repo in shared_repos:
repo.user_perm = check_permission(repo.props.repo_id, username)
# sort repos by last modify time
shared_repos.sort(lambda x, y: cmp(y.props.last_modified, x.props.last_modified))
return shared_repos
# repo permissoin
def check_permission(repo_id, user):
"""
Check whether user has permission to access repo.
Return values can be 'rw' or 'r' or None.
"""
try:
ret = seafserv_threaded_rpc.check_permission(repo_id, user)
except SearpcError:
ret = None
return ret
def is_personal_repo(repo_id):
"""
Check whether repo is personal repo.
"""
try:
owner = seafserv_threaded_rpc.get_repo_owner(repo_id)
except SearpcError:
owner = ''
return True if owner else False
# shared repo
def list_share_repos(user, share_type, start, limit):
try:
ret = seafserv_threaded_rpc.list_share_repos(user, share_type,
start, limit)
except SearpcError:
ret = []
return ret
def remove_share(repo_id, from_user, to_user):
seafserv_threaded_rpc.remove_share(repo_id, from_user, to_user)
def unshare_group_repo(repo_id, group_id, from_user):
return seafserv_threaded_rpc.group_unshare_repo(repo_id, int(group_id),
from_user)
def list_personal_shared_repos(user, user_type, start, limit):
"""
List personal repos that user share with others.
If `user_type` is 'from_email', list repos user shares to others;
If `user_type` is 'to_email', list repos others share to user.
"""
share_repos = list_share_repos(user, user_type, start, limit)
for repo in share_repos:
repo.user_perm = check_permission(repo.props.repo_id, user)
share_repos.sort(lambda x, y: cmp(y.last_modified, x.last_modified))
return share_repos
def list_org_shared_repos(org_id, user, user_type, start, limit):
"""
List org repos that user share with others.
If `user_type` is 'from_email', list repos user shares to others;
If `user_type` is 'to_email', list repos others sahre to user.
"""
try:
share_repos = seafserv_threaded_rpc.list_org_share_repos(org_id,
user, user_type,
start, limit)
except SearpcError:
share_repos = []
for repo in share_repos:
repo.user_perm = check_permission(repo.props.repo_id, user)
share_repos.sort(lambda x, y: cmp(y.last_modified, x.last_modified))
return share_repos
# dir
def list_dir_by_path(repo_id, commit_id, path):
try:
ret = seafserv_threaded_rpc.list_dir_by_path(repo_id, commit_id, path)
except SearpcError:
ret = None
return ret
# file
def post_empty_file(repo_id, parent_dir, file_name, user):
"""
Return true if successfully make a new file, otherwise false.
"""
try:
ret = seafserv_threaded_rpc.post_empty_file(repo_id, parent_dir,
file_name, user)
except SearpcError as e:
logger.error(e)
ret = -1
return True if ret == 0 else False
def del_file(repo_id, parent_dir, file_name, user):
"""
Return true if successfully delete a file, otherwise false.
"""
try:
ret = seafserv_threaded_rpc.del_file(repo_id, parent_dir,
file_name, user)
except SearpcError as e:
logger.error(e)
ret = -1
return True if ret == 0 else False
# misc functions
def is_valid_filename(file_or_dir):
"""
Check whether file name or directory name is valid.
"""
try:
ret = seafserv_threaded_rpc.is_valid_filename('', file_or_dir)
except SearpcError:
ret = 0
return ret
def get_file_size(store_id, version, file_id):
try:
fs = seafserv_threaded_rpc.get_file_size(store_id, version, file_id)
except SearpcError as e:
fs = 0
return fs
def get_file_id_by_path(repo_id, path):
try:
ret = seafserv_threaded_rpc.get_file_id_by_path(repo_id, path)
except SearpcError as e:
ret = ''
return ret
def get_related_users_by_repo(repo_id):
"""Give a repo id, returns a list of users of:
- the repo owner
- members of groups to which the repo is shared
- users to which the repo is shared
"""
owner = seafserv_threaded_rpc.get_repo_owner(repo_id)
if not owner:
# Can't happen
return []
users = [owner]
groups = get_shared_groups_by_repo(repo_id)
for group in groups:
members = get_group_members(group.id)
for member in members:
if member.user_name not in users:
users.append(member.user_name)
share_repos = list_share_repos(owner, 'from_email', -1, -1)
for repo in share_repos:
if repo.repo_id == repo_id:
if repo.user not in users:
users.append(repo.user)
return users
def get_related_users_by_org_repo(org_id, repo_id):
"""Org version of get_related_users_by_repo
"""
owner = get_org_repo_owner(repo_id)
if not owner:
# Can't happen
return []
users = [owner]
groups = get_org_groups_by_repo(org_id, repo_id)
for group in groups:
members = get_group_members(group.id)
for member in members:
if member.user_name not in users:
users.append(member.user_name)
share_repos = seafserv_threaded_rpc.list_org_share_repos(org_id, \
owner, 'from_email', -1, -1)
for repo in share_repos:
if repo.repo_id == repo_id:
if repo.user not in users:
users.append(repo.user)
return users
# quota
def check_quota(repo_id, delta=0):
try:
ret = seafserv_threaded_rpc.check_quota(repo_id, delta)
except SearpcError as e:
logger.error(e)
ret = -1
return ret
def get_user_quota(user):
try:
ret = seafserv_threaded_rpc.get_user_quota(user)
except SearpcError as e:
logger.error(e)
ret = 0
return ret
def get_user_quota_usage(user):
try:
ret = seafserv_threaded_rpc.get_user_quota_usage(user)
except SearpcError as e:
logger.error(e)
ret = 0
return ret
def get_user_share_usage(user):
try:
ret = seafserv_threaded_rpc.get_user_share_usage(user)
except SearpcError as e:
logger.error(e)
ret = 0
return ret
# access token
def web_get_access_token(repo_id, obj_id, op, username, use_onetime=1):
try:
ret = seafserv_rpc.web_get_access_token(repo_id, obj_id, op, username, use_onetime)
except SearpcError as e:
ret = ''
return ret
# password management
def unset_repo_passwd(repo_id, user):
"""
Remove user password of a encrypt repo.
Arguments:
- `repo_id`: encrypt repo id
- `user`: username
"""
try:
ret = seafserv_threaded_rpc.unset_passwd(repo_id, user)
except SearpcError as e:
ret = -1
return ret
def is_passwd_set(repo_id, user):
try:
ret = seafserv_rpc.is_passwd_set(repo_id, user)
except SearpcError as e:
ret = -1
return True if ret == 1 else False
# repo history limit
def get_repo_history_limit(repo_id):
try:
ret = seafserv_threaded_rpc.get_repo_history_limit(repo_id)
except SearpcError as e:
ret = -1
return ret
def set_repo_history_limit(repo_id, days):
try:
ret = seafserv_threaded_rpc.set_repo_history_limit(repo_id, days)
except SearpcError as e:
ret = -1
return ret