1# Copyright (c) 2012-2016 Seafile Ltd. 2# encoding: utf-8 3import re 4import logging 5 6from django import forms 7from django.core.mail import send_mail 8from django.utils import translation 9from django.utils.encoding import smart_text 10from django.utils.translation import ugettext_lazy as _ 11from django.conf import settings 12from django.contrib.sites.shortcuts import get_current_site 13from seaserv import ccnet_threaded_rpc, unset_repo_passwd, \ 14 seafile_api, ccnet_api 15from constance import config 16from registration import signals 17 18from seahub.auth import login 19from seahub.constants import DEFAULT_USER, DEFAULT_ORG, DEFAULT_ADMIN 20from seahub.profile.models import Profile, DetailedProfile 21from seahub.role_permissions.models import AdminRole 22from seahub.role_permissions.utils import get_enabled_role_permissions_by_role, \ 23 get_enabled_admin_role_permissions_by_role 24from seahub.utils import is_user_password_strong, get_site_name, \ 25 clear_token, get_system_admins, is_pro_version, IS_EMAIL_CONFIGURED 26from seahub.utils.mail import send_html_email_with_dj_template 27from seahub.utils.licenseparse import user_number_over_limit 28from seahub.share.models import ExtraSharePermission 29 30try: 31 from seahub.settings import CLOUD_MODE 32except ImportError: 33 CLOUD_MODE = False 34try: 35 from seahub.settings import MULTI_TENANCY 36except ImportError: 37 MULTI_TENANCY = False 38 39logger = logging.getLogger(__name__) 40 41ANONYMOUS_EMAIL = 'Anonymous' 42 43UNUSABLE_PASSWORD = '!' # This will never be a valid hash 44 45 46class UserManager(object): 47 48 def create_user(self, email, password=None, is_staff=False, is_active=False): 49 """ 50 Creates and saves a User with given username and password. 51 """ 52 # Lowercasing email address to avoid confusion. 53 email = email.lower() 54 55 user = User(email=email) 56 user.is_staff = is_staff 57 user.is_active = is_active 58 user.set_password(password) 59 user.save() 60 61 return self.get(email=email) 62 63 def update_role(self, email, role): 64 """ 65 If user has a role, update it; or create a role for user. 66 """ 67 ccnet_api.update_role_emailuser(email, role) 68 return self.get(email=email) 69 70 def create_superuser(self, email, password): 71 u = self.create_user(email, password, is_staff=True, is_active=True) 72 return u 73 74 def get_superusers(self): 75 """Return a list of admins. 76 """ 77 emailusers = ccnet_threaded_rpc.get_superusers() 78 79 user_list = [] 80 for e in emailusers: 81 user = User(e.email) 82 user.id = e.id 83 user.is_staff = e.is_staff 84 user.is_active = e.is_active 85 user.ctime = e.ctime 86 user_list.append(user) 87 88 return user_list 89 90 def get(self, email=None, id=None): 91 if not email and not id: 92 raise User.DoesNotExist('User matching query does not exits.') 93 94 if email: 95 emailuser = ccnet_threaded_rpc.get_emailuser(email) 96 if id: 97 emailuser = ccnet_threaded_rpc.get_emailuser_by_id(id) 98 if not emailuser: 99 raise User.DoesNotExist('User matching query does not exits.') 100 101 user = User(emailuser.email) 102 user.id = emailuser.id 103 user.enc_password = emailuser.password 104 user.is_staff = emailuser.is_staff 105 user.is_active = emailuser.is_active 106 user.ctime = emailuser.ctime 107 user.org = emailuser.org 108 user.source = emailuser.source 109 user.role = emailuser.role 110 user.reference_id = emailuser.reference_id 111 112 if user.is_staff: 113 try: 114 role_obj = AdminRole.objects.get_admin_role(emailuser.email) 115 admin_role = role_obj.role 116 except AdminRole.DoesNotExist: 117 admin_role = DEFAULT_ADMIN 118 119 user.admin_role = admin_role 120 else: 121 user.admin_role = '' 122 123 return user 124 125 126class UserPermissions(object): 127 def __init__(self, user): 128 self.user = user 129 130 def _get_user_role(self): 131 org_role = self.user.org_role 132 if org_role is None: 133 return self.user.role 134 135 if self.user.role == '' or self.user.role == DEFAULT_USER: 136 if org_role == DEFAULT_ORG: 137 return DEFAULT_USER 138 else: 139 return org_role 140 else: 141 return self.user.role 142 143 def _get_perm_by_roles(self, perm_name): 144 role = self._get_user_role() 145 return get_enabled_role_permissions_by_role(role)[perm_name] 146 147 def can_add_repo(self): 148 return self._get_perm_by_roles('can_add_repo') 149 150 def can_add_group(self): 151 return self._get_perm_by_roles('can_add_group') 152 153 def can_generate_share_link(self): 154 return self._get_perm_by_roles('can_generate_share_link') 155 156 def can_generate_upload_link(self): 157 return self._get_perm_by_roles('can_generate_upload_link') 158 159 def can_use_global_address_book(self): 160 return self._get_perm_by_roles('can_use_global_address_book') 161 162 def can_view_org(self): 163 if MULTI_TENANCY: 164 return True if self.user.org is not None else False 165 166 if CLOUD_MODE: 167 return False 168 169 return self._get_perm_by_roles('can_view_org') 170 171 def can_add_public_repo(self): 172 """ Check if user can create public repo or share existed repo to public. 173 174 Used when MULTI_TENANCY feature is NOT enabled. 175 """ 176 177 if CLOUD_MODE: 178 if MULTI_TENANCY: 179 return True 180 else: 181 return False 182 elif self.user.is_staff: 183 return True 184 elif self._get_perm_by_roles('can_add_public_repo') and \ 185 bool(config.ENABLE_USER_CREATE_ORG_REPO): 186 return True 187 else: 188 return False 189 190 def can_drag_drop_folder_to_sync(self): 191 return self._get_perm_by_roles('can_drag_drop_folder_to_sync') 192 193 def can_connect_with_android_clients(self): 194 return self._get_perm_by_roles('can_connect_with_android_clients') 195 196 def can_connect_with_ios_clients(self): 197 return self._get_perm_by_roles('can_connect_with_ios_clients') 198 199 def can_connect_with_desktop_clients(self): 200 return self._get_perm_by_roles('can_connect_with_desktop_clients') 201 202 def can_invite_guest(self): 203 return self._get_perm_by_roles('can_invite_guest') 204 205 def can_export_files_via_mobile_client(self): 206 return self._get_perm_by_roles('can_export_files_via_mobile_client') 207 208 def role_quota(self): 209 return self._get_perm_by_roles('role_quota') 210 211 def can_send_share_link_mail(self): 212 if not IS_EMAIL_CONFIGURED: 213 return False 214 215 return self._get_perm_by_roles('can_send_share_link_mail') 216 217 def storage_ids(self): 218 return self._get_perm_by_roles('storage_ids') 219 220 def can_publish_repo(self): 221 if not settings.ENABLE_WIKI: 222 return False 223 224 return self._get_perm_by_roles('can_publish_repo') 225 226 227class AdminPermissions(object): 228 def __init__(self, user): 229 self.user = user 230 231 def can_view_system_info(self): 232 return get_enabled_admin_role_permissions_by_role(self.user.admin_role)['can_view_system_info'] 233 234 def can_view_statistic(self): 235 return get_enabled_admin_role_permissions_by_role(self.user.admin_role)['can_view_statistic'] 236 237 def can_config_system(self): 238 return get_enabled_admin_role_permissions_by_role(self.user.admin_role)['can_config_system'] 239 240 def can_manage_library(self): 241 return get_enabled_admin_role_permissions_by_role(self.user.admin_role)['can_manage_library'] 242 243 def can_manage_user(self): 244 return get_enabled_admin_role_permissions_by_role(self.user.admin_role)['can_manage_user'] 245 246 def can_update_user(self): 247 return get_enabled_admin_role_permissions_by_role(self.user.admin_role)['can_update_user'] 248 249 def can_manage_group(self): 250 return get_enabled_admin_role_permissions_by_role(self.user.admin_role)['can_manage_group'] 251 252 def can_view_user_log(self): 253 return get_enabled_admin_role_permissions_by_role(self.user.admin_role)['can_view_user_log'] 254 255 def can_view_admin_log(self): 256 return get_enabled_admin_role_permissions_by_role(self.user.admin_role)['can_view_admin_log'] 257 258 def other_permission(self): 259 return get_enabled_admin_role_permissions_by_role(self.user.admin_role)['other_permission'] 260 261 262class User(object): 263 is_staff = False 264 is_active = False 265 is_superuser = False 266 groups = [] 267 org = None 268 objects = UserManager() 269 270 @property 271 def org_role(self): 272 if not MULTI_TENANCY: 273 return None 274 275 if not hasattr(self, '_cached_orgs'): 276 self._cached_orgs = ccnet_api.get_orgs_by_user(self.username) 277 278 if not self._cached_orgs: 279 return None 280 281 if not hasattr(self, '_cached_org_role'): 282 from seahub_extra.organizations.models import OrgSettings 283 self._cached_org_role = OrgSettings.objects.get_role_by_org( 284 self._cached_orgs[0]) 285 286 return self._cached_org_role 287 288 @property 289 def contact_email(self): 290 if not hasattr(self, '_cached_contact_email'): 291 self._cached_contact_email = email2contact_email(self.username) 292 293 return self._cached_contact_email 294 295 @property 296 def name(self): 297 if not hasattr(self, '_cached_nickname'): 298 # convert raw string to unicode obj 299 self._cached_nickname = smart_text(email2nickname(self.username)) 300 301 return self._cached_nickname 302 303 class DoesNotExist(Exception): 304 pass 305 306 def __init__(self, email): 307 self.username = email 308 self.email = email 309 self.permissions = UserPermissions(self) 310 self.admin_permissions = AdminPermissions(self) 311 312 def __unicode__(self): 313 return self.username 314 315 @property 316 def is_anonymous(self): 317 """ 318 Always returns False. This is a way of comparing User objects to 319 anonymous users. 320 """ 321 return False 322 323 @property 324 def is_authenticated(self): 325 """ 326 Always return True. This is a way to tell if the user has been 327 authenticated in templates. 328 """ 329 return True 330 331 def save(self): 332 emailuser = ccnet_api.get_emailuser(self.username) 333 if emailuser and emailuser.source.lower() in ("db", "ldapimport"): 334 if not hasattr(self, 'password'): 335 self.set_unusable_password() 336 337 if emailuser.source == "DB": 338 source = "DB" 339 else: 340 source = "LDAP" 341 342 if not self.is_active: 343 # clear web api and repo sync token 344 # when inactive an user 345 try: 346 clear_token(self.username) 347 except Exception as e: 348 logger.error(e) 349 350 result_code = ccnet_threaded_rpc.update_emailuser(source, 351 emailuser.id, 352 self.password, 353 int(self.is_staff), 354 int(self.is_active)) 355 else: 356 result_code = ccnet_threaded_rpc.add_emailuser(self.username, 357 self.password, 358 int(self.is_staff), 359 int(self.is_active)) 360 # -1 stands for failed; 0 stands for success 361 return result_code 362 363 def delete(self): 364 """ 365 When delete user, we should also delete group relationships. 366 """ 367 if self.source == "DB": 368 source = "DB" 369 else: 370 source = "LDAP" 371 372 username = self.username 373 374 orgs = [] 375 if is_pro_version(): 376 orgs = ccnet_api.get_orgs_by_user(username) 377 378 # remove owned repos 379 owned_repos = [] 380 if orgs: 381 for org in orgs: 382 owned_repos += seafile_api.get_org_owned_repo_list(org.org_id, 383 username) 384 else: 385 owned_repos += seafile_api.get_owned_repo_list(username) 386 387 for r in owned_repos: 388 seafile_api.remove_repo(r.id) 389 390 # remove shared in repos 391 shared_in_repos = [] 392 if orgs: 393 for org in orgs: 394 org_id = org.org_id 395 shared_in_repos = seafile_api.get_org_share_in_repo_list(org_id, username, -1, -1) 396 397 for r in shared_in_repos: 398 seafile_api.org_remove_share(org_id, r.repo_id, r.user, username) 399 else: 400 shared_in_repos = seafile_api.get_share_in_repo_list(username, -1, -1) 401 for r in shared_in_repos: 402 seafile_api.remove_share(r.repo_id, r.user, username) 403 ExtraSharePermission.objects.filter(share_to=username).delete() 404 405 # clear web api and repo sync token 406 # when delete user 407 try: 408 clear_token(self.username) 409 except Exception as e: 410 logger.error(e) 411 412 # remove current user from joined groups 413 ccnet_api.remove_group_user(username) 414 415 ccnet_api.remove_emailuser(source, username) 416 signals.user_deleted.send(sender=self.__class__, username=username) 417 418 Profile.objects.delete_profile_by_user(username) 419 if config.ENABLE_TERMS_AND_CONDITIONS: 420 from termsandconditions.models import UserTermsAndConditions 421 UserTermsAndConditions.objects.filter(username=username).delete() 422 self.delete_user_options(username) 423 424 def get_username(self): 425 return self.username 426 427 def delete_user_options(self, username): 428 """Remove user's all options. 429 """ 430 from seahub.options.models import UserOptions 431 UserOptions.objects.filter(email=username).delete() 432 433 def get_and_delete_messages(self): 434 messages = [] 435 return messages 436 437 def set_password(self, raw_password): 438 if raw_password is None: 439 self.set_unusable_password() 440 else: 441 self.password = '%s' % raw_password 442 443 # clear web api and repo sync token 444 # when user password change 445 try: 446 clear_token(self.username) 447 except Exception as e: 448 logger.error(e) 449 450 def check_password(self, raw_password): 451 """ 452 Returns a boolean of whether the raw_password was correct. Handles 453 encryption formats behind the scenes. 454 """ 455 # Backwards-compatibility check. Older passwords won't include the 456 # algorithm or salt. 457 458 # if '$' not in self.password: 459 # is_correct = (self.password == \ 460 # get_hexdigest('sha1', '', raw_password)) 461 # return is_correct 462 return (ccnet_threaded_rpc.validate_emailuser(self.username, raw_password) == 0) 463 464 def set_unusable_password(self): 465 # Sets a value that will never be a valid hash 466 self.password = UNUSABLE_PASSWORD 467 468 def email_user(self, subject, message, from_email=None): 469 "Sends an e-mail to this User." 470 send_mail(subject, message, from_email, [self.email]) 471 472 def freeze_user(self, notify_admins=False): 473 self.is_active = False 474 self.save() 475 476 if notify_admins: 477 admins = get_system_admins() 478 for u in admins: 479 # save current language 480 cur_language = translation.get_language() 481 482 # get and active user language 483 user_language = Profile.objects.get_user_language(u.email) 484 translation.activate(user_language) 485 486 send_html_email_with_dj_template(u.email, 487 subject=_('Account %(account)s froze on %(site)s.') % { 488 "account": self.email, 489 "site": get_site_name()}, 490 dj_template='sysadmin/user_freeze_email.html', 491 context={'user': self.email}) 492 493 # restore current language 494 translation.activate(cur_language) 495 496 def remove_repo_passwds(self): 497 """ 498 Remove all repo decryption passwords stored on server. 499 """ 500 from seahub.utils import get_user_repos 501 owned_repos, shared_repos, groups_repos, public_repos = get_user_repos(self.email) 502 503 def has_repo(repos, repo): 504 for r in repos: 505 if repo.id == r.id: 506 return True 507 return False 508 509 passwd_setted_repos = [] 510 for r in owned_repos + shared_repos + groups_repos + public_repos: 511 if not has_repo(passwd_setted_repos, r) and r.encrypted and \ 512 seafile_api.is_password_set(r.id, self.email): 513 passwd_setted_repos.append(r) 514 515 for r in passwd_setted_repos: 516 unset_repo_passwd(r.id, self.email) 517 518 def remove_org_repo_passwds(self, org_id): 519 """ 520 Remove all org repo decryption passwords stored on server. 521 """ 522 from seahub.utils import get_user_repos 523 owned_repos, shared_repos, groups_repos, public_repos = get_user_repos(self.email, org_id=org_id) 524 525 def has_repo(repos, repo): 526 for r in repos: 527 if repo.id == r.id: 528 return True 529 return False 530 531 passwd_setted_repos = [] 532 for r in owned_repos + shared_repos + groups_repos + public_repos: 533 if not has_repo(passwd_setted_repos, r) and r.encrypted and \ 534 seafile_api.is_password_set(r.id, self.email): 535 passwd_setted_repos.append(r) 536 537 for r in passwd_setted_repos: 538 unset_repo_passwd(r.id, self.email) 539 540 541class AuthBackend(object): 542 543 def get_user_with_import(self, username): 544 emailuser = ccnet_api.get_emailuser_with_import(username) 545 if not emailuser: 546 raise User.DoesNotExist('User matching query does not exits.') 547 548 user = User(emailuser.email) 549 user.id = emailuser.id 550 user.enc_password = emailuser.password 551 user.is_staff = emailuser.is_staff 552 user.is_active = emailuser.is_active 553 user.ctime = emailuser.ctime 554 user.org = emailuser.org 555 user.source = emailuser.source 556 user.role = emailuser.role 557 558 if user.is_staff: 559 try: 560 role_obj = AdminRole.objects.get_admin_role(emailuser.email) 561 admin_role = role_obj.role 562 except AdminRole.DoesNotExist: 563 admin_role = DEFAULT_ADMIN 564 565 user.admin_role = admin_role 566 else: 567 user.admin_role = '' 568 569 return user 570 571 def get_user(self, username): 572 try: 573 user = self.get_user_with_import(username) 574 except User.DoesNotExist: 575 user = None 576 return user 577 578 def authenticate(self, username=None, password=None): 579 user = self.get_user(username) 580 if not user: 581 return None 582 583 if user.check_password(password): 584 return user 585 586 587# Register related 588class RegistrationBackend(object): 589 """ 590 A registration backend which follows a simple workflow: 591 592 1. User signs up, inactive account is created. 593 594 2. Email is sent to user with activation link. 595 596 3. User clicks activation link, account is now active. 597 598 Using this backend requires that 599 600 * ``registration`` be listed in the ``INSTALLED_APPS`` setting 601 (since this backend makes use of models defined in this 602 application). 603 604 * The setting ``ACCOUNT_ACTIVATION_DAYS`` be supplied, specifying 605 (as an integer) the number of days from registration during 606 which a user may activate their account (after that period 607 expires, activation will be disallowed). 608 609 * The creation of the templates 610 ``registration/activation_email_subject.txt`` and 611 ``registration/activation_email.txt``, which will be used for 612 the activation email. See the notes for this backends 613 ``register`` method for details regarding these templates. 614 615 Additionally, registration can be temporarily closed by adding the 616 setting ``REGISTRATION_OPEN`` and setting it to 617 ``False``. Omitting this setting, or setting it to ``True``, will 618 be interpreted as meaning that registration is currently open and 619 permitted. 620 621 Internally, this is accomplished via storing an activation key in 622 an instance of ``registration.models.RegistrationProfile``. See 623 that model and its custom manager for full documentation of its 624 fields and supported operations. 625 626 """ 627 def register(self, request, **kwargs): 628 """ 629 Given a username, email address and password, register a new 630 user account, which will initially be inactive. 631 632 Along with the new ``User`` object, a new 633 ``registration.models.RegistrationProfile`` will be created, 634 tied to that ``User``, containing the activation key which 635 will be used for this account. 636 637 An email will be sent to the supplied email address; this 638 email should contain an activation link. The email will be 639 rendered using two templates. See the documentation for 640 ``RegistrationProfile.send_activation_email()`` for 641 information about these templates and the contexts provided to 642 them. 643 644 After the ``User`` and ``RegistrationProfile`` are created and 645 the activation email is sent, the signal 646 ``registration.signals.user_registered`` will be sent, with 647 the new ``User`` as the keyword argument ``user`` and the 648 class of this backend as the sender. 649 650 """ 651 email, password = kwargs['email'], kwargs['password1'] 652 username = email 653 site = get_current_site(request) 654 655 from registration.models import RegistrationProfile 656 if bool(config.ACTIVATE_AFTER_REGISTRATION) is True: 657 # since user will be activated after registration, 658 # so we will not use email sending, just create acitvated user 659 new_user = RegistrationProfile.objects.create_active_user(username, email, 660 password, site, 661 send_email=False) 662 # login the user 663 new_user.backend = settings.AUTHENTICATION_BACKENDS[0] 664 665 login(request, new_user) 666 else: 667 # create inactive user, user can be activated by admin, or through activated email 668 new_user = RegistrationProfile.objects.create_inactive_user(username, email, 669 password, site, 670 send_email=config.REGISTRATION_SEND_MAIL) 671 672 # userid = kwargs['userid'] 673 # if userid: 674 # ccnet_threaded_rpc.add_binding(new_user.username, userid) 675 676 if settings.REQUIRE_DETAIL_ON_REGISTRATION: 677 name = kwargs.get('name', '') 678 department = kwargs.get('department', '') 679 telephone = kwargs.get('telephone', '') 680 note = kwargs.get('note', '') 681 Profile.objects.add_or_update(new_user.username, name, note) 682 DetailedProfile.objects.add_detailed_profile(new_user.username, 683 department, 684 telephone) 685 686 signals.user_registered.send(sender=self.__class__, 687 user=new_user, 688 request=request) 689 return new_user 690 691 def activate(self, request, activation_key): 692 """ 693 Given an an activation key, look up and activate the user 694 account corresponding to that key (if possible). 695 696 After successful activation, the signal 697 ``registration.signals.user_activated`` will be sent, with the 698 newly activated ``User`` as the keyword argument ``user`` and 699 the class of this backend as the sender. 700 701 """ 702 from registration.models import RegistrationProfile 703 activated = RegistrationProfile.objects.activate_user(activation_key) 704 if activated: 705 signals.user_activated.send(sender=self.__class__, 706 user=activated, 707 request=request) 708 # login the user 709 activated.backend = settings.AUTHENTICATION_BACKENDS[0] 710 login(request, activated) 711 712 return activated 713 714 def registration_allowed(self, request): 715 """ 716 Indicate whether account registration is currently permitted, 717 based on the value of the setting ``REGISTRATION_OPEN``. This 718 is determined as follows: 719 720 * If ``REGISTRATION_OPEN`` is not specified in settings, or is 721 set to ``True``, registration is permitted. 722 723 * If ``REGISTRATION_OPEN`` is both specified and set to 724 ``False``, registration is not permitted. 725 726 """ 727 return getattr(settings, 'REGISTRATION_OPEN', True) 728 729 def get_form_class(self, request): 730 """ 731 Return the default form class used for user registration. 732 733 """ 734 return RegistrationForm 735 736 def post_registration_redirect(self, request, user): 737 """ 738 Return the name of the URL to redirect to after successful 739 user registration. 740 741 """ 742 return ('registration_complete', (), {}) 743 744 def post_activation_redirect(self, request, user): 745 """ 746 Return the name of the URL to redirect to after successful 747 account activation. 748 749 """ 750 return ('libraries', (), {}) 751 752 753class RegistrationForm(forms.Form): 754 """ 755 Form for registering a new user account. 756 757 Validates that the requested email is not already in use, and 758 requires the password to be entered twice to catch typos. 759 """ 760 attrs_dict = {'class': 'input'} 761 762 email = forms.CharField(widget=forms.TextInput(attrs=dict(attrs_dict, maxlength=75)), 763 label=_("Email address")) 764 765 userid = forms.RegexField(regex=r'^\w+$', 766 max_length=40, 767 required=False, 768 widget=forms.TextInput(), 769 label=_("Username"), 770 error_messages={'invalid': _("This value must be of length 40")}) 771 772 password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False), 773 label=_("Password")) 774 password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False), 775 label=_("Password (again)")) 776 777 @classmethod 778 def allow_register(self, email): 779 prog = re.compile(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)", 780 re.IGNORECASE) 781 return False if prog.match(email) is None else True 782 783 def clean_email(self): 784 if user_number_over_limit(): 785 raise forms.ValidationError(_("The number of users exceeds the limit.")) 786 787 email = self.cleaned_data['email'] 788 if not self.allow_register(email): 789 raise forms.ValidationError(_("Enter a valid email address.")) 790 791 emailuser = ccnet_threaded_rpc.get_emailuser(email) 792 if not emailuser: 793 return self.cleaned_data['email'] 794 else: 795 raise forms.ValidationError(_("User %s already exists.") % email) 796 797 def clean_userid(self): 798 if self.cleaned_data['userid'] and len(self.cleaned_data['userid']) != 40: 799 raise forms.ValidationError(_("Invalid user id.")) 800 return self.cleaned_data['userid'] 801 802 def clean_password1(self): 803 if 'password1' in self.cleaned_data: 804 pwd = self.cleaned_data['password1'] 805 806 if bool(config.USER_STRONG_PASSWORD_REQUIRED) is True: 807 if bool(is_user_password_strong(pwd)) is True: 808 return pwd 809 else: 810 raise forms.ValidationError( 811 _(("%(pwd_len)s characters or more, include " 812 "%(num_types)s types or more of these: " 813 "letters(case sensitive), numbers, and symbols")) % 814 {'pwd_len': config.USER_PASSWORD_MIN_LENGTH, 815 'num_types': config.USER_PASSWORD_STRENGTH_LEVEL}) 816 else: 817 return pwd 818 819 def clean_password2(self): 820 """ 821 Verifiy that the values entered into the two password fields 822 match. Note that an error here will end up in 823 ``non_field_errors()`` because it doesn't apply to a single 824 field. 825 826 """ 827 if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data: 828 if self.cleaned_data['password1'] != self.cleaned_data['password2']: 829 raise forms.ValidationError(_("The two password fields didn't match.")) 830 return self.cleaned_data 831 832 833class DetailedRegistrationForm(RegistrationForm): 834 attrs_dict = {'class': 'input'} 835 836 try: 837 from seahub.settings import REGISTRATION_DETAILS_MAP 838 except ImportError: 839 REGISTRATION_DETAILS_MAP = None 840 841 if REGISTRATION_DETAILS_MAP: 842 name_required = REGISTRATION_DETAILS_MAP.get('name', False) 843 dept_required = REGISTRATION_DETAILS_MAP.get('department', False) 844 tele_required = REGISTRATION_DETAILS_MAP.get('telephone', False) 845 note_required = REGISTRATION_DETAILS_MAP.get('note', False) 846 else: 847 # Backward compatible 848 name_required = dept_required = tele_required = note_required = True 849 850 name = forms.CharField(widget=forms.TextInput( 851 attrs=dict(attrs_dict, maxlength=64)), label=_("name"), 852 required=name_required) 853 department = forms.CharField(widget=forms.TextInput( 854 attrs=dict(attrs_dict, maxlength=512)), label=_("department"), 855 required=dept_required) 856 telephone = forms.CharField(widget=forms.TextInput( 857 attrs=dict(attrs_dict, maxlength=100)), label=_("telephone"), 858 required=tele_required) 859 note = forms.CharField(widget=forms.TextInput( 860 attrs=dict(attrs_dict, maxlength=100)), label=_("note"), 861 required=note_required) 862 863# Move here to avoid circular import 864from seahub.base.templatetags.seahub_tags import email2nickname, \ 865 email2contact_email 866