1# Copyright (c) 2012-2016 Seafile Ltd. 2# -*- coding: utf-8 -*- 3import stat 4import logging 5from collections import namedtuple 6 7import seaserv 8from seaserv import seafile_api, ccnet_api 9 10from seahub.constants import ( 11 PERMISSION_PREVIEW, PERMISSION_PREVIEW_EDIT, 12 PERMISSION_READ, PERMISSION_READ_WRITE, PERMISSION_ADMIN, 13 REPO_STATUS_NORMAL, REPO_STATUS_READ_ONLY 14) 15from seahub.utils import EMPTY_SHA1, is_org_context, is_pro_version 16from seahub.base.models import RepoSecretKey 17from seahub.base.templatetags.seahub_tags import email2nickname 18 19from seahub.settings import ENABLE_STORAGE_CLASSES, \ 20 STORAGE_CLASS_MAPPING_POLICY, ENABLE_FOLDER_PERM 21 22logger = logging.getLogger(__name__) 23 24 25def normalize_repo_status_code(status): 26 if status == 0: 27 return REPO_STATUS_NORMAL 28 elif status == 1: 29 return REPO_STATUS_READ_ONLY 30 else: 31 return '' 32 33 34def normalize_repo_status_str(status): 35 if status == 'normal': 36 return 0 37 elif status == 'read-only': 38 return 1 39 else: 40 return '' 41 42 43def get_available_repo_perms(): 44 perms = [PERMISSION_READ, PERMISSION_READ_WRITE, PERMISSION_ADMIN] 45 if is_pro_version(): 46 perms += [PERMISSION_PREVIEW, PERMISSION_PREVIEW_EDIT] 47 48 return perms 49 50 51def parse_repo_perm(perm): 52 RP = namedtuple('RepoPerm', [ 53 'can_download', 'can_upload', # download/uplaod files/folders 54 'can_edit_on_web', # edit files on web 55 'can_copy', # copy files/folders on web 56 'can_preview', # preview files on web 57 'can_generate_share_link', # generate share link 58 ]) 59 60 RP.can_download = True if perm in [ 61 PERMISSION_READ, PERMISSION_READ_WRITE, PERMISSION_ADMIN] else False 62 RP.can_upload = True if perm in [ 63 PERMISSION_READ_WRITE, PERMISSION_ADMIN] else False 64 RP.can_edit_on_web = True if perm in [ 65 PERMISSION_READ_WRITE, PERMISSION_ADMIN, PERMISSION_PREVIEW_EDIT 66 ] else False 67 RP.can_copy = True if perm in [ 68 PERMISSION_READ, PERMISSION_READ_WRITE, PERMISSION_ADMIN, 69 ] else False 70 RP.can_preview = True if perm in [ 71 PERMISSION_READ, PERMISSION_READ_WRITE, PERMISSION_ADMIN, 72 PERMISSION_PREVIEW, PERMISSION_PREVIEW_EDIT 73 ] else False 74 RP.can_generate_share_link = True if perm in [ 75 PERMISSION_READ_WRITE, PERMISSION_READ, PERMISSION_ADMIN, 76 PERMISSION_PREVIEW, PERMISSION_PREVIEW_EDIT 77 ] else False 78 return RP 79 80def list_dir_by_path(cmmt, path): 81 if cmmt.root_id == EMPTY_SHA1: 82 return [] 83 else: 84 dirs = seafile_api.list_dir_by_commit_and_path(cmmt.repo_id, cmmt.id, path) 85 return dirs if dirs else [] 86 87def get_sub_repo_abbrev_origin_path(repo_name, origin_path): 88 """Return abbrev path for sub repo based on `repo_name` and `origin_path`. 89 90 Arguments: 91 - `repo_id`: 92 - `origin_path`: 93 """ 94 if len(origin_path) > 20: 95 abbrev_path = origin_path[-20:] 96 return repo_name + '/...' + abbrev_path 97 else: 98 return repo_name + origin_path 99 100def get_repo_owner(request, repo_id): 101 if is_org_context(request): 102 repo_owner = seafile_api.get_org_repo_owner(repo_id) 103 else: 104 # for admin panel 105 # administrator may get org repo's owner 106 repo_owner = seafile_api.get_repo_owner(repo_id) 107 if not repo_owner: 108 repo_owner = seafile_api.get_org_repo_owner(repo_id) 109 110 return repo_owner 111 112def is_repo_owner(request, repo_id, username): 113 return username == get_repo_owner(request, repo_id) 114 115def get_repo_shared_users(repo_id, repo_owner, include_groups=True): 116 """Return a list contains users and group users. Repo owner is ommited. 117 """ 118 ret = [] 119 users = seafile_api.list_repo_shared_to(repo_owner, repo_id) 120 ret += [x.user for x in users] 121 if include_groups: 122 for e in seafile_api.list_repo_shared_group_by_user(repo_owner, repo_id): 123 g_members = seaserv.get_group_members(e.group_id) 124 ret += [x.user_name for x in g_members if x.user_name != repo_owner] 125 126 return list(set(ret)) 127 128def get_library_storages(request): 129 """ Return info of storages can be used. 130 131 1. If not enable user role feature OR 132 haven't configured `storage_ids` option in user role setting: 133 134 Return storage info getted from seafile_api. 135 And always put the default storage as the first item in the returned list. 136 137 2. If have configured `storage_ids` option in user role setting: 138 139 Only return storage info in `storage_ids`. 140 Filter out the wrong stotage id(s). 141 Not change the order of the `storage_ids` list. 142 """ 143 144 if not is_pro_version(): 145 return [] 146 147 if not ENABLE_STORAGE_CLASSES: 148 return [] 149 150 # get all storages info 151 try: 152 storage_classes = seafile_api.get_all_storage_classes() 153 except Exception as e: 154 logger.error(e) 155 return [] 156 157 all_storages = [] 158 for storage in storage_classes: 159 storage_info = { 160 'storage_id': storage.storage_id, 161 'storage_name': storage.storage_name, 162 'is_default': storage.is_default, 163 } 164 if storage.is_default: 165 all_storages.insert(0, storage_info) 166 else: 167 all_storages.append(storage_info) 168 169 if STORAGE_CLASS_MAPPING_POLICY == 'USER_SELECT': 170 171 return all_storages 172 173 elif STORAGE_CLASS_MAPPING_POLICY == 'ROLE_BASED': 174 user_role_storage_ids = request.user.permissions.storage_ids() 175 if not user_role_storage_ids: 176 return [] 177 178 user_role_storages = [] 179 for user_role_storage_id in user_role_storage_ids: 180 for storage in all_storages: 181 if storage['storage_id'] == user_role_storage_id: 182 user_role_storages.append(storage) 183 continue 184 185 return user_role_storages 186 187 else: 188 # STORAGE_CLASS_MAPPING_POLICY == 'REPO_ID_MAPPING' 189 return [] 190 191def get_locked_files_by_dir(request, repo_id, folder_path): 192 """ Get locked files in a folder 193 194 Returns: 195 A dict contains locked file name and locker owner. 196 197 locked_files = { 198 'file_name': 'lock_owner'; 199 ... 200 } 201 """ 202 203 username = request.user.username 204 205 # get lock files 206 dir_id = seafile_api.get_dir_id_by_path(repo_id, folder_path) 207 dirents = seafile_api.list_dir_with_perm(repo_id, 208 folder_path, dir_id, username, -1, -1) 209 210 locked_files = {} 211 for dirent in dirents: 212 if dirent.is_locked: 213 locked_files[dirent.obj_name] = dirent.lock_owner 214 215 return locked_files 216 217def get_sub_folder_permission_by_dir(request, repo_id, parent_dir): 218 """ Get sub folder permission in a folder 219 220 Returns: 221 A dict contains folder name and permission. 222 223 folder_permission_dict = { 224 'folder_name_1': 'r'; 225 'folder_name_2': 'rw'; 226 ... 227 } 228 """ 229 username = request.user.username 230 dir_id = seafile_api.get_dir_id_by_path(repo_id, parent_dir) 231 dirents = seafile_api.list_dir_with_perm(repo_id, 232 parent_dir, dir_id, username, -1, -1) 233 234 folder_permission_dict = {} 235 for dirent in dirents: 236 if stat.S_ISDIR(dirent.mode): 237 folder_permission_dict[dirent.obj_name] = dirent.permission 238 239 return folder_permission_dict 240 241def get_shared_groups_by_repo(repo_id, org_id=None): 242 if not org_id: 243 group_ids = seafile_api.get_shared_group_ids_by_repo( 244 repo_id) 245 else: 246 group_ids = seafile_api.org_get_shared_group_ids_by_repo(org_id, 247 repo_id) 248 249 if not group_ids: 250 return [] 251 252 groups = [] 253 for group_id in group_ids: 254 group = ccnet_api.get_group(int(group_id)) 255 if group: 256 groups.append(group) 257 258 return groups 259 260def get_related_users_by_repo(repo_id, org_id=None): 261 """ Return all users who can view this library. 262 263 1. repo owner 264 2. users repo has been shared to 265 3. members of groups repo has been shared to 266 """ 267 268 users = [] 269 270 # 1. users repo has been shared to 271 if org_id and org_id > 0: 272 users.extend(seafile_api.org_get_shared_users_by_repo(org_id, repo_id)) 273 owner = seafile_api.get_org_repo_owner(repo_id) 274 else: 275 users.extend(seafile_api.get_shared_users_by_repo(repo_id)) 276 owner = seafile_api.get_repo_owner(repo_id) 277 278 # 2. repo owner 279 if owner not in users: 280 users.append(owner) 281 282 # 3. members of groups repo has been shared to 283 groups = get_shared_groups_by_repo(repo_id, org_id) 284 for group in groups: 285 members = ccnet_api.get_group_members(group.id) 286 for member in members: 287 if member.user_name not in users: 288 users.append(member.user_name) 289 290 return users 291 292# TODO 293def is_valid_repo_id_format(repo_id): 294 return len(repo_id) == 36 295 296def can_set_folder_perm_by_user(username, repo, repo_owner): 297 """ user can get/update/add/delete folder perm feature must comply with the following 298 setting: ENABLE_FOLDER_PERM 299 repo:repo is not virtual 300 permission: is admin or repo owner. 301 """ 302 if not ENABLE_FOLDER_PERM: 303 return False 304 if repo.is_virtual: 305 return False 306 is_admin = is_repo_admin(username, repo.id) 307 if username != repo_owner and not is_admin: 308 return False 309 return True 310 311def add_encrypted_repo_secret_key_to_database(repo_id, password): 312 try: 313 if not RepoSecretKey.objects.get_secret_key(repo_id): 314 # get secret_key, then save it to database 315 secret_key = seafile_api.get_secret_key(repo_id, password) 316 RepoSecretKey.objects.add_secret_key(repo_id, secret_key) 317 except Exception as e: 318 logger.error(e) 319 320 321def is_group_repo_staff(request, repo_id, username): 322 is_staff = False 323 324 repo_owner = get_repo_owner(request, repo_id) 325 326 if '@seafile_group' in repo_owner: 327 group_id = email2nickname(repo_owner) 328 is_staff = seaserv.check_group_staff(group_id, username) 329 330 return is_staff 331 332def repo_has_been_shared_out(request, repo_id): 333 334 has_been_shared_out = False 335 username = request.user.username 336 337 if is_org_context(request): 338 org_id = request.user.org.org_id 339 340 is_inner_org_pub_repo = False 341 # check if current repo is pub-repo 342 org_pub_repos = seafile_api.list_org_inner_pub_repos_by_owner( 343 org_id, username) 344 for org_pub_repo in org_pub_repos: 345 if repo_id == org_pub_repo.id: 346 is_inner_org_pub_repo = True 347 break 348 349 if seafile_api.org_repo_has_been_shared(repo_id, including_groups=True) or is_inner_org_pub_repo: 350 has_been_shared_out = True 351 else: 352 if seafile_api.repo_has_been_shared(repo_id, including_groups=True) or \ 353 (not request.cloud_mode and seafile_api.is_inner_pub_repo(repo_id)): 354 has_been_shared_out = True 355 356 return has_been_shared_out 357 358# TODO 359from seahub.share.utils import is_repo_admin 360