def get_user(self, user_id): user = None try: user = User.objects.get(pk=user_id) _LDAPUser(self, user=user) # This sets user.ldap_user except User.DoesNotExist: pass return user
def _copy_groups(self, user): # Normally should use the following line: # user = self.backend.get_user(user.pk) # ... but this will save a database query _LDAPUser(self.backend, user=user) try: user.ldap_user._mirror_groups() except Exception as e: # _mirror_groups fails when ldap_user is not Active user.groups.clear() return user
def authenticate(self, request=None, username=None, password=None, **kwargs): if request is None: try: group = kwargs.get('group') if group.searh_user(username): return User(username=username) return None except: return None token = get_credentials(request, **kwargs) if token is not None: payload = get_payload(token, request) username = jwt_settings.JWT_PAYLOAD_GET_USERNAME_HANDLER(payload) ldap_user = _LDAPUser(self, username=username.strip(), request=request) if self.settings.USER_SEARCH is not None: user_dn = ldap_user._search_for_user_dn() elif self.settings.USER_DN_TEMPLATE is not None: user_dn = ldap_user._construct_simple_user_dn() else: user_dn = None if user_dn is not None: try: result_search = ldap_user.connection.search_s(user_dn, 0) if len(result_search) == 1 and result_search is not None: return User(username=username) return None except: return None if password or self.settings.PERMIT_EMPTY_PASSWORD: ldap_user = _LDAPUser(self, username=username.strip(), request=request) user = self.authenticate_ldap_user(ldap_user, password) else: logger.debug('Rejecting empty password for {}'.format(username)) user = None return user
def get_or_create_local_user(user): """ Create a local user if the username exists in the configured LDAP server. Returns the updated or newly created local django.contrib.auth.models.User """ warnings.warn( 'This is most likely going to be deprecated due to upcoming' 'demerger work', PendingDeprecationWarning) # instantiate the LDAPBackend model class # magically finds the LDAP server from settings.py ldap_backend = LDAPBackend() if user.find('@') != -1: ldap_user = _LDAPUser(ldap_backend, username="") result = ldap_user.connection.search_s( "dc=corporateict,dc=domain", scope=ldap_backend.ldap.SCOPE_SUBTREE, filterstr='(mail=' + user + ')', attrlist=[str("sAMAccountName").encode('ASCII')]) if result: user = result[0][1]["sAMAccountName"][0].lower() else: return None try: user = User.objects.get(username=user) except User.DoesNotExist: user = ldap_backend.populate_user(user) return user
def get_or_create_local_user(user): """ Create a local user if the username exists in the configured LDAP server. Returns the updated or newly created local django.contrib.auth.models.User """ warnings.warn('This is most likely going to be deprecated due to upcoming' 'demerger work', PendingDeprecationWarning) # instantiate the LDAPBackend model class # magically finds the LDAP server from settings.py ldap_backend = LDAPBackend() if user.find('@') != -1: ldap_user = _LDAPUser(ldap_backend, username="") result = ldap_user.connection.search_s( "dc=corporateict,dc=domain", scope=ldap_backend.ldap.SCOPE_SUBTREE, filterstr='(mail=' + user + ')', attrlist=[str("sAMAccountName").encode('ASCII')] ) if result: user = result[0][1]["sAMAccountName"][0].lower() else: return None try: user = User.objects.get(username=user) except User.DoesNotExist: user = ldap_backend.populate_user(user) return user
def query_ldap(email: str) -> List[str]: values = [] backend = next( (backend for backend in get_backends() if isinstance(backend, LDAPBackend)), None) if backend is not None: ldap_attrs = _LDAPUser(backend, backend.django_to_ldap_username(email)).attrs if ldap_attrs is None: values.append("No such user found") else: for django_field, ldap_field in settings.AUTH_LDAP_USER_ATTR_MAP.items( ): value = ldap_attrs.get(ldap_field, [ "LDAP field not present", ])[0] if django_field == "avatar": if isinstance(value, bytes): value = "(An avatar image file)" values.append("%s: %s" % (django_field, value)) if settings.LDAP_EMAIL_ATTR is not None: values.append( "%s: %s" % ('email', ldap_attrs[settings.LDAP_EMAIL_ATTR][0])) else: values.append("LDAP backend not configured on this server.") return values
def query_ldap(**options): email = options['email'] for backend in get_backends(): if isinstance(backend, LDAPBackend): ldap_attrs = _LDAPUser(backend, backend.django_to_ldap_username(email)).attrs if ldap_attrs is None: print("No such user found") else: for django_field, ldap_field in settings.AUTH_LDAP_USER_ATTR_MAP.items(): print("%s: %s" % (django_field, ldap_attrs[ldap_field]))
def authenticate(self, request=None, username=None, password=None, **kwargs): # Login with username@DOMAIN if LDAP_DOMAIN: if (username.find('@') == -1 or username.strip().split('@')[1].upper() != LDAP_DOMAIN): return None ldap_user = _LDAPUser(self, username=username.split('@')[0].strip()) # Login with username only else: if username.find('@') != -1: return None ldap_user = _LDAPUser(self, username=username.strip()) user = ldap_user.authenticate(password) return user
def handle(self, *args, **options): filter_string = options.get('filter', DEFAULT_FILTER) ldap_backend = LDAPBackend() ldap_user = _LDAPUser(backend=ldap_backend, username='') ldap_connection = ldap_user.connection # Initialize the LDAP controls for paging. # Note that we pass '' for the cookie because on first iteration, it starts out empty. ldap_controls = SimplePagedResultsControl(True, size=PAGE_SIZE, cookie='') while True: # Send search request # If you leave out the ATTRLIST it'll return all attributes which you have permissions to access. # You may want to adjust the scope level as well # (perhaps "ldap.SCOPE_SUBTREE", but it can reduce performance if you don't need it). message_id = ldap_connection.search_ext( base=settings.AUTH_LDAP_USER_SEARCH.base_dn, scope=ldap.SCOPE_SUBTREE, filterstr=filter_string, serverctrls=[ldap_controls]) # Pull the results from the search request rtype, rdata, rmsgid, server_controls = ldap_connection.result3( message_id) rdata = settings.AUTH_LDAP_USER_SEARCH._process_results(rdata) # Each "rdata" is a tuple of the form (dn, attrs), where dn is a string containing the # DN (distinguished name) of the entry, and attrs is a dictionary containing the attributes associated # with the entry. The keys of attrs are strings, and the associated values are lists of strings. for distinguished_name, attributes in rdata: username = "".join(attributes['uid']) print(f"{username} \t| {distinguished_name}") print(attributes) # Look through the returned controls and find the page controls. # This will also have our returned cookie which we need to make the next search request. page_controls = [ control for control in server_controls if control.controlType == SimplePagedResultsControl.controlType ] if not page_controls: print('Warning: Server ignores RFC 2696 control.') break # Ok, we did find the page control, yank the cookie from it and insert it into the control for our # next search. If however there is no cookie, we are done! cookie = page_controls[0].cookie ldap_controls.cookie = cookie if not cookie: break
def authenticate(self, request=None, username=None, password=None, **kwargs): if (username.find('@') == -1 or username.split('@')[1].upper() != LDAP2_DOMAIN): return None ldap_user = _LDAPUser(self, username=username.split('@')[0].strip()) user = ldap_user.authenticate(password) return user
def update_user(self, ldap_attrs): """ Create or update model User from LDAP data. django-auth-ldap library doesn't allow to populate user from raw LDAP data. Only by username. Sometimes we doesn't know username, but know other criteria, etc. DN. We should make LDAP request by DN, get LDAP data and create User model entry """ username = ldap_attrs.data.get( self.settings.USER_SEARCH_FIELD.lower())[0].lower() ldap_user = _LDAPUser(self, username) ldap_user._user_attrs = ldap_attrs built = not self.get_user_model().objects.filter( username=username).exists() ldap_user._get_or_create_user() return ldap_user._user, built
def import_all_users(self): ldap_backend = LDAPBackend() ldap_user = _LDAPUser(ldap_backend, username="") ldap_search = LDAPSearch(self.search_dn, ldap.SCOPE_SUBTREE, filterstr=self.filter, attrlist=[self.username_attribute]) results = ldap_search.execute(connection=ldap_user.connection) for result in results: if self.simple_search: search_term = result[1][self.username_attribute][0] else: search_term = result[0].split(',')[0].split('=')[1] ldap_backend.populate_user(search_term)
def search_ldap_for_user(username): """ Connects to LDAP using the settings and credentials defined for django_auth_ldap, then searches for a user matching the given username. Returns None if the user isn't found in LDAP, or tuple of (user_dn, ldap_attrs_dict) if it is. """ # This is pretty gnarly, but the way that django_auth_ldap's got all its LDAP code split up across its # various classes, this is probably much less nasty than trying to do it more directly. ldap_user = _LDAPUser(LDAPBackend(), username=username.strip()) results = LDAPSettings().USER_SEARCH.execute(ldap_user.connection, {'user': username}) if len(results) == 1: return results[0] return None
def query_ldap(**options: str) -> None: email = options['email'] for backend in get_backends(): if isinstance(backend, LDAPBackend): ldap_attrs = _LDAPUser(backend, backend.django_to_ldap_username(email)).attrs if ldap_attrs is None: print("No such user found") else: for django_field, ldap_field in settings.AUTH_LDAP_USER_ATTR_MAP.items(): value = ldap_attrs[ldap_field] if django_field == "avatar": if isinstance(value[0], bytes): value = "(An avatar image file)" print("%s: %s" % (django_field, value)) if settings.LDAP_EMAIL_ATTR is not None: print("%s: %s" % ('email', ldap_attrs[settings.LDAP_EMAIL_ATTR]))
def test_login_ldap(self): """ Test SRPMS can communicate to ANU LDAP, make sure you set up environment properly before running this test. """ # Test LDAP connection ldap_user = _LDAPUser(LDAPBackend(), username='******') self.assertEqual(ldap_user.attrs['uid'][0], 'u6513788', 'ANU LDAP server connection failed') if not settings.TEST: pass # Not under test environment, skip LDAP login testing else: # LDAP testing account is store in docker secret, we need to retrieve that first client = APIClient() ldap_test_username = settings.get_env('', 'LDAP_TEST_USERNAME_FILE') # Test LDAP login with valid credential response = client.post( '/api/accounts/token/', { 'username': ldap_test_username, 'password': settings.get_env('', 'LDAP_TEST_PASSWORD_FILE') }, format='json', secure=True, follow=True) self.assertEqual(response.status_code, status.HTTP_200_OK) # Test LDAP user attribute mapping srpms_user = SrpmsUser.objects.get(username=ldap_test_username) self.assertEqual(ldap_user.attrs['uid'][0], srpms_user.username) self.assertEqual(ldap_user.attrs['givenname'][0], srpms_user.first_name) self.assertEqual(ldap_user.attrs['sn'][0], srpms_user.last_name) self.assertEqual(ldap_user.attrs['mail'][0], srpms_user.email) # Test LDAP login with invalid credential response = client.post('/api/accounts/token/', { 'username': ldap_test_username, 'password': '******' }, format='json', secure=True) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def get_queryset(self): User = auth.get_user_model() queryset = User.objects.select_related("userprofile").prefetch_related( "userprofile__institutions") # load any LDAP backends backends = [b for b in auth.get_backends() if hasattr(b, "ldap")] # attempt to load groups from LDAP before yielding for user in queryset: for backend in backends: # doing this saves a database query over directly loading ldap_user = _LDAPUser(backend, user=user) try: ldap_user._mirror_groups() except Exception: # do nothing on failure to find user in backend pass yield user
def test_ldap_user_creation_also_creates_actor(settings, factories, mocker): actor = factories["federation.Actor"]() mocker.patch("funkwhale_api.users.models.create_actor", return_value=actor) mocker.patch( "django_auth_ldap.backend.LDAPBackend.ldap_to_django_username", return_value="hello", ) settings.AUTHENTICATION_BACKENDS += ( "django_auth_ldap.backend.LDAPBackend", ) # django-auth-ldap offers a populate_user signal we can use # to create our user actor if it does not exists ldap_backend = get_backends()[-1] ldap_user = backend._LDAPUser(ldap_backend, username="******") ldap_user._user_attrs = {"hello": "world"} ldap_user._get_or_create_user() ldap_user._user.refresh_from_db() assert ldap_user._user.actor == actor
def process_entry(self, dn, attrs): """Process an entry. The two arguments passed are the DN and a dictionary of attributes.""" # print(dn, attrs) username = "".join(attrs['uid']) # ToDo: change back to cn try: # fake an ldap user and set the internal user attributes, as if they were from LDAP ldap_user = _LDAPUser(self.ldap_backend, username=username) ldap_user._user_attrs = attrs # call populate user with the internal attributes # user = user.populate_user() # username = self.ldap_backend.ldap_to_django_username(username) (user, built) = self.ldap_backend.get_or_build_user(username, ldap_user) setattr(user, "ldap_user", ldap_user) user.save() # if not built: # create_user_profile_ldap(user, ldap_user) # assign_ldap_user_group(user, ldap_user) # user._get_or_create_user() # user = LDAPBackend().populate_user(username) if not user: print( 'ERROR: LDAPBackend().populate_user() did not find user with username=', username) print(repr(dn)) print(attrs) except: print("Error trying to sync user with username=", username) print(repr(dn)) print(attrs) raise
def handle(self, *args, **options): ldap_backend = LDAPBackend() dummy_user = _LDAPUser(ldap_backend, username="******") user_search = LDAPSearch(settings.AUTH_LDAP_USER_DN, ldap.SCOPE_SUBTREE, "(uid=*)") user_data = user_search.execute(dummy_user.connection) list_of_current_ldap_users = self.process_user_list(user_data) known_users = self.filter_django_users(User.objects.all()) num_deleted_users = 0 num_imported_users = 0 for user in known_users: if user.username in list_of_current_ldap_users: index = list_of_current_ldap_users.index(user.username) list_of_current_ldap_users.pop(index) else: if not user.is_superuser: user.delete() num_deleted_users += 1 # if there are any usernames left, we need to add new users to the database for username in list_of_current_ldap_users: django_user = ldap_backend.populate_user(username) if django_user is None: self.stderr.write( self.style.ERROR( f"Could not create user with username {username}")) continue ldap_backend.update_mail_addresses(django_user) ldap_backend.set_groups_of_user(django_user) num_imported_users += 1 self.stdout.write(self.style.SUCCESS("Import Complete")) self.stdout.write( self.style.SUCCESS( f"Imported {num_imported_users} users into the database.")) self.stdout.write( self.style.SUCCESS( f"Deleted {num_deleted_users} users from the database."))
def query_ldap(**options: str) -> None: email = options['email'] for backend in get_backends(): if isinstance(backend, LDAPBackend): ldap_attrs = _LDAPUser( backend, backend.django_to_ldap_username(email)).attrs if ldap_attrs is None: print("No such user found") else: for django_field, ldap_field in settings.AUTH_LDAP_USER_ATTR_MAP.items( ): value = ldap_attrs[ldap_field] if django_field == "avatar": if isinstance(value[0], bytes): value = "(An avatar image file)" print("%s: %s" % (django_field, value)) if settings.LDAP_EMAIL_ATTR is not None: print("%s: %s" % ('email', ldap_attrs[settings.LDAP_EMAIL_ATTR]))
def query_ldap(email: str) -> List[str]: values = [] backend = next((backend for backend in get_backends() if isinstance(backend, LDAPBackend)), None) if backend is not None: ldap_attrs = _LDAPUser(backend, backend.django_to_ldap_username(email)).attrs if ldap_attrs is None: values.append("No such user found") else: for django_field, ldap_field in settings.AUTH_LDAP_USER_ATTR_MAP.items(): value = ldap_attrs.get(ldap_field, ["LDAP field not present", ])[0] if django_field == "avatar": if isinstance(value, bytes): value = "(An avatar image file)" values.append("%s: %s" % (django_field, value)) if settings.LDAP_EMAIL_ATTR is not None: values.append("%s: %s" % ('email', ldap_attrs[settings.LDAP_EMAIL_ATTR][0])) else: values.append("LDAP backend not configured on this server.") return values
def ldap_change_password(request, user, **kwargs): enable_password_change = get_site_preferences( )["ldap__enable_password_change"] admin_password_change = get_site_preferences( )["ldap__admin_password_change"] admin_dn = get_site_preferences()["ldap__admin_dn"] admin_password = get_site_preferences()["ldap__admin_password"] if not enable_password_change: # Do nothing if password change in LDAP is disabled return if not hasattr(user, "ldap_user"): # Add ldap_user relation to user if not available yet # This can happen on password reset, when the user is acted upon # but was never logged-in from django_auth_ldap.backend import LDAPBackend, _LDAPUser # noqa user.ldap_user = _LDAPUser(LDAPBackend(), user=user) # Get old and new password from submitted form # We rely on allauth already having validated the form before emitting the signal old = request.POST.get("oldpassword", None) new = request.POST["password1"] # Determine as which user to make the password change if old and not admin_password_change: # If we are changing a password as user, use their credentials # except if the preference mandates always using admin credentials bind_dn, password = user.ldap_user.dn, old elif admin_dn: # In all other cases, use admin credentials if available bind_dn, password = admin_dn, admin_password else: # If not available, try using the regular LDAP auth credentials bind_dn, password = None, None with TemporaryBind(user.ldap_user, bind_dn, password) as conn: conn.passwd_s(user.ldap_user.dn, old, new)
def ldap_users(self): search = settings.ldap_settings.LDAP_PIXIEDUST_ALL_USERS for dn, attrs in search.execute(self.conn): user_id = attrs[ settings.ldap_settings.LDAP_PIXIEDUST_USERNAME_DN_ATTRIBUTE][0] yield _LDAPUser(self.backend, username=user_id)
def accounts_register(request: HttpRequest) -> HttpResponse: key = request.POST['key'] confirmation = Confirmation.objects.get(confirmation_key=key) prereg_user = confirmation.content_object email = prereg_user.email realm_creation = prereg_user.realm_creation password_required = prereg_user.password_required is_realm_admin = prereg_user.invited_as_admin or realm_creation try: validators.validate_email(email) except ValidationError: return render(request, "zerver/invalid_email.html", context={"invalid_email": True}) if realm_creation: # For creating a new realm, there is no existing realm or domain realm = None else: realm = get_realm(get_subdomain(request)) if realm is None or realm != prereg_user.realm: return render_confirmation_key_error( request, ConfirmationKeyException(ConfirmationKeyException.DOES_NOT_EXIST)) try: email_allowed_for_realm(email, realm) except DomainNotAllowedForRealmError: return render(request, "zerver/invalid_email.html", context={"realm_name": realm.name, "closed_domain": True}) except DisposableEmailError: return render(request, "zerver/invalid_email.html", context={"realm_name": realm.name, "disposable_emails_not_allowed": True}) except EmailContainsPlusError: return render(request, "zerver/invalid_email.html", context={"realm_name": realm.name, "email_contains_plus": True}) if realm.deactivated: # The user is trying to register for a deactivated realm. Advise them to # contact support. return redirect_to_deactivation_notice() try: validate_email_for_realm(realm, email) except ValidationError: # nocoverage # We need to add a test for this. return HttpResponseRedirect(reverse('django.contrib.auth.views.login') + '?email=' + urllib.parse.quote_plus(email)) name_validated = False full_name = None if request.POST.get('from_confirmation'): try: del request.session['authenticated_full_name'] except KeyError: pass if realm is not None and realm.is_zephyr_mirror_realm: # For MIT users, we can get an authoritative name from Hesiod. # Technically we should check that this is actually an MIT # realm, but we can cross that bridge if we ever get a non-MIT # zephyr mirroring realm. hesiod_name = compute_mit_user_fullname(email) form = RegistrationForm( initial={'full_name': hesiod_name if "@" not in hesiod_name else ""}, realm_creation=realm_creation) name_validated = True elif settings.POPULATE_PROFILE_VIA_LDAP: for backend in get_backends(): if isinstance(backend, LDAPBackend): try: ldap_username = backend.django_to_ldap_username(email) except ZulipLDAPException: logging.warning("New account email %s could not be found in LDAP" % (email,)) form = RegistrationForm(realm_creation=realm_creation) break ldap_attrs = _LDAPUser(backend, ldap_username).attrs try: ldap_full_name = ldap_attrs[settings.AUTH_LDAP_USER_ATTR_MAP['full_name']][0] request.session['authenticated_full_name'] = ldap_full_name name_validated = True # We don't use initial= here, because if the form is # complete (that is, no additional fields need to be # filled out by the user) we want the form to validate, # so they can be directly registered without having to # go through this interstitial. form = RegistrationForm({'full_name': ldap_full_name}, realm_creation=realm_creation) # FIXME: This will result in the user getting # validation errors if they have to enter a password. # Not relevant for ONLY_SSO, though. break except TypeError: # Let the user fill out a name and/or try another backend form = RegistrationForm(realm_creation=realm_creation) elif 'full_name' in request.POST: form = RegistrationForm( initial={'full_name': request.POST.get('full_name')}, realm_creation=realm_creation ) else: form = RegistrationForm(realm_creation=realm_creation) else: postdata = request.POST.copy() if name_changes_disabled(realm): # If we populate profile information via LDAP and we have a # verified name from you on file, use that. Otherwise, fall # back to the full name in the request. try: postdata.update({'full_name': request.session['authenticated_full_name']}) name_validated = True except KeyError: pass form = RegistrationForm(postdata, realm_creation=realm_creation) if not (password_auth_enabled(realm) and password_required): form['password'].field.required = False if form.is_valid(): if password_auth_enabled(realm): password = form.cleaned_data['password'] else: # SSO users don't need no passwords password = None if realm_creation: string_id = form.cleaned_data['realm_subdomain'] realm_name = form.cleaned_data['realm_name'] realm = do_create_realm(string_id, realm_name) setup_initial_streams(realm) setup_realm_internal_bots(realm) assert(realm is not None) full_name = form.cleaned_data['full_name'] short_name = email_to_username(email) default_stream_group_names = request.POST.getlist('default_stream_group') default_stream_groups = lookup_default_stream_groups(default_stream_group_names, realm) timezone = "" if 'timezone' in request.POST and request.POST['timezone'] in get_all_timezones(): timezone = request.POST['timezone'] if 'source_realm' in request.POST and request.POST["source_realm"] != "on": source_profile = get_source_profile(email, request.POST["source_realm"]) else: source_profile = None if not realm_creation: try: existing_user_profile = get_user(email, realm) # type: Optional[UserProfile] except UserProfile.DoesNotExist: existing_user_profile = None else: existing_user_profile = None return_data = {} # type: Dict[str, bool] if ldap_auth_enabled(realm): # If the user was authenticated using an external SSO # mechanism like Google or GitHub auth, then authentication # will have already been done before creating the # PreregistrationUser object with password_required=False, and # so we don't need to worry about passwords. # # If instead the realm is using EmailAuthBackend, we will # set their password above. # # But if the realm is using LDAPAuthBackend, we need to verify # their LDAP password (which will, as a side effect, create # the user account) here using authenticate. auth_result = authenticate(request, username=email, password=password, realm=realm, return_data=return_data) if auth_result is not None: # Since we'll have created a user, we now just log them in. return login_and_go_to_home(request, auth_result) if return_data.get("outside_ldap_domain") and email_auth_enabled(realm): # If both the LDAP and Email auth backends are # enabled, and the user's email is outside the LDAP # domain, then the intent is to create a user in the # realm with their email outside the LDAP organization # (with e.g. a password stored in the Zulip database, # not LDAP). So we fall through and create the new # account. # # It's likely that we can extend this block to the # Google and GitHub auth backends with no code changes # other than here. pass else: # TODO: This probably isn't going to give a # user-friendly error message, but it doesn't # particularly matter, because the registration form # is hidden for most users. return HttpResponseRedirect(reverse('django.contrib.auth.views.login') + '?email=' + urllib.parse.quote_plus(email)) if existing_user_profile is not None and existing_user_profile.is_mirror_dummy: user_profile = existing_user_profile do_activate_user(user_profile) do_change_password(user_profile, password) do_change_full_name(user_profile, full_name, user_profile) do_set_user_display_setting(user_profile, 'timezone', timezone) # TODO: When we clean up the `do_activate_user` code path, # make it respect invited_as_admin / is_realm_admin. else: user_profile = do_create_user(email, password, realm, full_name, short_name, prereg_user=prereg_user, is_realm_admin=is_realm_admin, tos_version=settings.TOS_VERSION, timezone=timezone, newsletter_data={"IP": request.META['REMOTE_ADDR']}, default_stream_groups=default_stream_groups, source_profile=source_profile) if realm_creation: bulk_add_subscriptions([realm.signup_notifications_stream], [user_profile]) send_initial_realm_messages(realm) # Because for realm creation, registration happens on the # root domain, we need to log them into the subdomain for # their new realm. return redirect_and_log_into_subdomain(realm, full_name, email) # This dummy_backend check below confirms the user is # authenticating to the correct subdomain. auth_result = authenticate(username=user_profile.email, realm=realm, return_data=return_data, use_dummy_backend=True) if return_data.get('invalid_subdomain'): # By construction, this should never happen. logging.error("Subdomain mismatch in registration %s: %s" % ( realm.subdomain, user_profile.email,)) return redirect('/') return login_and_go_to_home(request, auth_result) return render( request, 'zerver/register.html', context={'form': form, 'email': email, 'key': key, 'full_name': request.session.get('authenticated_full_name', None), 'lock_name': name_validated and name_changes_disabled(realm), # password_auth_enabled is normally set via our context processor, # but for the registration form, there is no logged in user yet, so # we have to set it here. 'creating_new_team': realm_creation, 'password_required': password_auth_enabled(realm) and password_required, 'password_auth_enabled': password_auth_enabled(realm), 'root_domain_available': is_root_domain_available(), 'default_stream_groups': get_default_stream_groups(realm), 'accounts': get_accounts_for_email(email), 'MAX_REALM_NAME_LENGTH': str(Realm.MAX_REALM_NAME_LENGTH), 'MAX_NAME_LENGTH': str(UserProfile.MAX_NAME_LENGTH), 'MAX_PASSWORD_LENGTH': str(form.MAX_PASSWORD_LENGTH), 'MAX_REALM_SUBDOMAIN_LENGTH': str(Realm.MAX_REALM_SUBDOMAIN_LENGTH) } )
def ldap_users(self): search = settings.ldap_settings.LDAP_PIXIEDUST_ALL_USERS for dn, attrs in search.execute(self.conn): user_id = attrs[settings.ldap_settings.LDAP_PIXIEDUST_USERNAME_DN_ATTRIBUTE][0] yield _LDAPUser(self.backend, username=user_id)
def accounts_register(request): # type: (HttpRequest) -> HttpResponse key = request.POST['key'] confirmation = Confirmation.objects.get(confirmation_key=key) prereg_user = confirmation.content_object email = prereg_user.email realm_creation = prereg_user.realm_creation try: existing_user_profile = get_user_profile_by_email(email) except UserProfile.DoesNotExist: existing_user_profile = None validators.validate_email(email) # If OPEN_REALM_CREATION is enabled all user sign ups should go through the # special URL with domain name so that REALM can be identified if multiple realms exist unique_open_realm = get_unique_open_realm() if unique_open_realm is not None: realm = unique_open_realm elif prereg_user.referred_by: # If someone invited you, you are joining their realm regardless # of your e-mail address. realm = prereg_user.referred_by.realm elif prereg_user.realm: # You have a realm set, even though nobody referred you. This # happens if you sign up through a special URL for an open realm. realm = prereg_user.realm elif realm_creation: # For creating a new realm, there is no existing realm or domain realm = None elif settings.REALMS_HAVE_SUBDOMAINS: realm = get_realm(get_subdomain(request)) else: realm = get_realm_by_email_domain(email) if realm and not email_allowed_for_realm(email, realm): return render(request, "zerver/closed_realm.html", context={"closed_domain_name": realm.name}) if realm and realm.deactivated: # The user is trying to register for a deactivated realm. Advise them to # contact support. return render(request, "zerver/deactivated.html", context={ "deactivated_domain_name": realm.name, "zulip_administrator": settings.ZULIP_ADMINISTRATOR }) try: if existing_user_profile is not None and existing_user_profile.is_mirror_dummy: # Mirror dummy users to be activated must be inactive is_inactive(email) else: # Other users should not already exist at all. user_email_is_unique(email) except ValidationError: return HttpResponseRedirect( reverse('django.contrib.auth.views.login') + '?email=' + urllib.parse.quote_plus(email)) name_validated = False full_name = None if request.POST.get('from_confirmation'): try: del request.session['authenticated_full_name'] except KeyError: pass if realm is not None and realm.is_zephyr_mirror_realm: # For MIT users, we can get an authoritative name from Hesiod. # Technically we should check that this is actually an MIT # realm, but we can cross that bridge if we ever get a non-MIT # zephyr mirroring realm. hesiod_name = compute_mit_user_fullname(email) form = RegistrationForm( initial={ 'full_name': hesiod_name if "@" not in hesiod_name else "" }) name_validated = True elif settings.POPULATE_PROFILE_VIA_LDAP: for backend in get_backends(): if isinstance(backend, LDAPBackend): ldap_attrs = _LDAPUser( backend, backend.django_to_ldap_username(email)).attrs try: ldap_full_name = ldap_attrs[ settings.AUTH_LDAP_USER_ATTR_MAP['full_name']][0] request.session[ 'authenticated_full_name'] = ldap_full_name name_validated = True # We don't use initial= here, because if the form is # complete (that is, no additional fields need to be # filled out by the user) we want the form to validate, # so they can be directly registered without having to # go through this interstitial. form = RegistrationForm({'full_name': ldap_full_name}) # FIXME: This will result in the user getting # validation errors if they have to enter a password. # Not relevant for ONLY_SSO, though. break except TypeError: # Let the user fill out a name and/or try another backend form = RegistrationForm() elif 'full_name' in request.POST: form = RegistrationForm( initial={'full_name': request.POST.get('full_name')}) else: form = RegistrationForm() else: postdata = request.POST.copy() if name_changes_disabled(realm): # If we populate profile information via LDAP and we have a # verified name from you on file, use that. Otherwise, fall # back to the full name in the request. try: postdata.update( {'full_name': request.session['authenticated_full_name']}) name_validated = True except KeyError: pass form = RegistrationForm(postdata) if not password_auth_enabled(realm): form['password'].field.required = False if form.is_valid(): if password_auth_enabled(realm): password = form.cleaned_data['password'] else: # SSO users don't need no passwords password = None if realm_creation: string_id = form.cleaned_data['realm_subdomain'] realm_name = form.cleaned_data['realm_name'] org_type = int(form.cleaned_data['realm_org_type']) realm = do_create_realm(string_id, realm_name, org_type=org_type)[0] set_default_streams(realm, settings.DEFAULT_NEW_REALM_STREAMS) full_name = form.cleaned_data['full_name'] short_name = email_to_username(email) first_in_realm = len( UserProfile.objects.filter(realm=realm, is_bot=False)) == 0 # FIXME: sanitize email addresses and fullname if existing_user_profile is not None and existing_user_profile.is_mirror_dummy: user_profile = existing_user_profile do_activate_user(user_profile) do_change_password(user_profile, password) do_change_full_name(user_profile, full_name) else: user_profile = do_create_user( email, password, realm, full_name, short_name, prereg_user=prereg_user, tos_version=settings.TOS_VERSION, newsletter_data={"IP": request.META['REMOTE_ADDR']}) if first_in_realm: do_change_is_admin(user_profile, True) if realm_creation and settings.REALMS_HAVE_SUBDOMAINS: # Because for realm creation, registration happens on the # root domain, we need to log them into the subdomain for # their new realm. return redirect_and_log_into_subdomain(realm, full_name, email) # This dummy_backend check below confirms the user is # authenticating to the correct subdomain. return_data = {} # type: Dict[str, bool] auth_result = authenticate(username=user_profile.email, realm_subdomain=realm.subdomain, return_data=return_data, use_dummy_backend=True) if return_data.get('invalid_subdomain'): # By construction, this should never happen. logging.error("Subdomain mismatch in registration %s: %s" % ( realm.subdomain, user_profile.email, )) return redirect('/') login(request, auth_result) return HttpResponseRedirect(realm.uri + reverse('zerver.views.home.home')) return render( request, 'zerver/register.html', context={ 'form': form, 'email': email, 'key': key, 'full_name': request.session.get('authenticated_full_name', None), 'lock_name': name_validated and name_changes_disabled(realm), # password_auth_enabled is normally set via our context processor, # but for the registration form, there is no logged in user yet, so # we have to set it here. 'creating_new_team': realm_creation, 'realms_have_subdomains': settings.REALMS_HAVE_SUBDOMAINS, 'password_auth_enabled': password_auth_enabled(realm), 'MAX_REALM_NAME_LENGTH': str(Realm.MAX_REALM_NAME_LENGTH), 'MAX_NAME_LENGTH': str(UserProfile.MAX_NAME_LENGTH), 'MAX_PASSWORD_LENGTH': str(form.MAX_PASSWORD_LENGTH), 'MAX_REALM_SUBDOMAIN_LENGTH': str(Realm.MAX_REALM_SUBDOMAIN_LENGTH) })
def mass_ldap_import(): """Add utility code for mass import from ldap.""" from django_auth_ldap.backend import LDAPBackend, _LDAPUser # noqa Person = apps.get_model("core", "Person") # Abuse pre-configured search object as general LDAP interface backend = LDAPBackend() connection = _LDAPUser(backend, "").connection # Synchronise all groups first if get_site_preferences()["ldap__enable_group_sync"]: ldap_groups = backend.settings.GROUP_SEARCH.execute(connection) group_objects = ldap_sync_from_groups(ldap_groups) # Create lookup table as cache for later code group_dict = {obj.ldap_dn: obj for obj in group_objects} # Guess LDAP username field from user filter uid_field = re.search( r"([a-zA-Z]+)=%\(user\)s", backend.settings.USER_SEARCH.searches[0].filterstr).group(1) # Synchronise user data for all found users ldap_users = backend.settings.USER_SEARCH.execute(connection, {"user": "******"}, escape=False) for dn, attrs in tqdm(ldap_users, desc="Sync. user infos", **TQDM_DEFAULTS): uid = attrs[uid_field][0] # Prepare an empty LDAPUser object with the target username ldap_user = _LDAPUser(backend, username=uid) # Get existing or new User object and pre-populate user, created = backend.get_or_build_user(uid, ldap_user) ldap_user._user = user ldap_user._attrs = attrs ldap_user._dn = dn ldap_user._populate_user_from_attributes() user.save() try: with transaction.atomic(): person = ldap_sync_from_user(user, dn, attrs) except Person.DoesNotExist: logger.warn(f"No matching person for user {user.username}") continue except Person.MultipleObjectsReturned: logger.error( f"More than one matching person for user {user.username}") continue except (DataError, IntegrityError, KeyError, ValueError) as e: logger.error( f"Data error while synchronising user {user.username}:\n{e}") continue else: logger.info(f"Successfully imported user {uid}") # Synchronise group memberships now if get_site_preferences()["ldap__enable_group_sync"]: member_attr = getattr(backend.settings.GROUP_TYPE, "member_attr", "memberUid") owner_attr = get_site_preferences()["ldap__group_sync_owner_attr"] for ldap_group in tqdm( ldap_groups, desc="Sync. group members", total=len(ldap_groups), **TQDM_DEFAULTS, ): dn, attrs = ldap_group if dn not in group_dict: logger.warning( f"Skip {dn} because there are no groups with this dn.") continue group = group_dict[dn] ldap_members = [_.lower() for _ in attrs[member_attr] ] if member_attr in attrs else [] if member_attr.lower() == "memberuid": members = Person.objects.filter( user__username__in=ldap_members) else: members = Person.objects.filter(ldap_dn__in=ldap_members) if get_site_preferences()["ldap__group_sync_owner_attr"]: ldap_owners = [_.lower() for _ in attrs[owner_attr] ] if owner_attr in attrs else [] if get_site_preferences( )["ldap__group_sync_owner_attr_type"] == "uid": owners = Person.objects.filter( user__username__in=ldap_owners) elif get_site_preferences( )["ldap__group_sync_owner_attr_type"] == "dn": owners = Person.objects.filter(ldap_dn__in=ldap_owners) group.members.set(members) if get_site_preferences()["ldap__group_sync_owner_attr"]: group.owners.set(owners) group.save() logger.info(f"Set group members of group {group}") # Synchronise primary groups all_persons = set(Person.objects.all()) for person in tqdm(all_persons, desc="Sync. primary groups", **TQDM_DEFAULTS): person.auto_select_primary_group() Person.objects.bulk_update(all_persons, ("primary_group", )) logger.info("Commiting transaction; this can take some time.")
def accounts_register(request): # type: (HttpRequest) -> HttpResponse key = request.POST['key'] confirmation = Confirmation.objects.get(confirmation_key=key) prereg_user = confirmation.content_object email = prereg_user.email realm_creation = prereg_user.realm_creation try: existing_user_profile = get_user_profile_by_email(email) except UserProfile.DoesNotExist: existing_user_profile = None validators.validate_email(email) # If OPEN_REALM_CREATION is enabled all user sign ups should go through the # special URL with domain name so that REALM can be identified if multiple realms exist unique_open_realm = get_unique_open_realm() if unique_open_realm is not None: realm = unique_open_realm domain = realm.domain elif prereg_user.referred_by: # If someone invited you, you are joining their realm regardless # of your e-mail address. realm = prereg_user.referred_by.realm domain = realm.domain if not email_allowed_for_realm(email, realm): return render_to_response("zerver/closed_realm.html", {"closed_domain_name": realm.name}) elif prereg_user.realm: # You have a realm set, even though nobody referred you. This # happens if you sign up through a special URL for an open # realm. domain = prereg_user.realm.domain realm = get_realm(domain) else: domain = resolve_email_to_domain(email) realm = get_realm(domain) if realm and realm.deactivated: # The user is trying to register for a deactivated realm. Advise them to # contact support. return render_to_response("zerver/deactivated.html", {"deactivated_domain_name": realm.name, "zulip_administrator": settings.ZULIP_ADMINISTRATOR}) try: if existing_user_profile is not None and existing_user_profile.is_mirror_dummy: # Mirror dummy users to be activated must be inactive is_inactive(email) else: # Other users should not already exist at all. user_email_is_unique(email) except ValidationError: return HttpResponseRedirect(reverse('django.contrib.auth.views.login') + '?email=' + urllib.parse.quote_plus(email)) name_validated = False full_name = None if request.POST.get('from_confirmation'): try: del request.session['authenticated_full_name'] except KeyError: pass if realm is not None and realm.is_zephyr_mirror_realm and domain == "mit.edu": # for MIT users, we can get an authoritative name from Hesiod hesiod_name = compute_mit_user_fullname(email) form = RegistrationForm( initial={'full_name': hesiod_name if "@" not in hesiod_name else ""}) name_validated = True elif settings.POPULATE_PROFILE_VIA_LDAP: for backend in get_backends(): if isinstance(backend, LDAPBackend): ldap_attrs = _LDAPUser(backend, backend.django_to_ldap_username(email)).attrs try: ldap_full_name = ldap_attrs[settings.AUTH_LDAP_USER_ATTR_MAP['full_name']][0] request.session['authenticated_full_name'] = ldap_full_name name_validated = True # We don't use initial= here, because if the form is # complete (that is, no additional fields need to be # filled out by the user) we want the form to validate, # so they can be directly registered without having to # go through this interstitial. form = RegistrationForm({'full_name': ldap_full_name}) # FIXME: This will result in the user getting # validation errors if they have to enter a password. # Not relevant for ONLY_SSO, though. break except TypeError: # Let the user fill out a name and/or try another backend form = RegistrationForm() elif 'full_name' in request.POST: form = RegistrationForm( initial={'full_name': request.POST.get('full_name')} ) else: form = RegistrationForm() else: postdata = request.POST.copy() if name_changes_disabled(realm): # If we populate profile information via LDAP and we have a # verified name from you on file, use that. Otherwise, fall # back to the full name in the request. try: postdata.update({'full_name': request.session['authenticated_full_name']}) name_validated = True except KeyError: pass form = RegistrationForm(postdata) if not password_auth_enabled(realm): form['password'].field.required = False if form.is_valid(): if password_auth_enabled(realm): password = form.cleaned_data['password'] else: # SSO users don't need no passwords password = None if realm_creation: string_id = form.cleaned_data['realm_subdomain'] realm_name = form.cleaned_data['realm_name'] org_type = int(form.cleaned_data['realm_org_type']) domain = split_email_to_domain(email) realm = do_create_realm(string_id, realm_name, org_type=org_type, domain=domain)[0] set_default_streams(realm, settings.DEFAULT_NEW_REALM_STREAMS) full_name = form.cleaned_data['full_name'] short_name = email_to_username(email) first_in_realm = len(UserProfile.objects.filter(realm=realm, is_bot=False)) == 0 # FIXME: sanitize email addresses and fullname if existing_user_profile is not None and existing_user_profile.is_mirror_dummy: try: user_profile = existing_user_profile do_activate_user(user_profile) do_change_password(user_profile, password) do_change_full_name(user_profile, full_name) except UserProfile.DoesNotExist: user_profile = do_create_user(email, password, realm, full_name, short_name, prereg_user=prereg_user, tos_version=settings.TOS_VERSION, newsletter_data={"IP": request.META['REMOTE_ADDR']}) else: user_profile = do_create_user(email, password, realm, full_name, short_name, prereg_user=prereg_user, tos_version=settings.TOS_VERSION, newsletter_data={"IP": request.META['REMOTE_ADDR']}) if first_in_realm: do_change_is_admin(user_profile, True) if realm_creation and settings.REALMS_HAVE_SUBDOMAINS: # Because for realm creation, registration happens on the # root domain, we need to log them into the subdomain for # their new realm. return redirect_and_log_into_subdomain(realm, full_name, email) # This dummy_backend check below confirms the user is # authenticating to the correct subdomain. return_data = {} # type: Dict[str, bool] auth_result = authenticate(username=user_profile.email, realm_subdomain=realm.subdomain, return_data=return_data, use_dummy_backend=True) if return_data.get('invalid_subdomain'): # By construction, this should never happen. logging.error("Subdomain mismatch in registration %s: %s" % ( realm.subdomain, user_profile.email,)) return redirect('/') login(request, auth_result) return HttpResponseRedirect(realm.uri + reverse('zerver.views.home')) return render_to_response('zerver/register.html', {'form': form, 'company_name': domain, 'email': email, 'key': key, 'full_name': request.session.get('authenticated_full_name', None), 'lock_name': name_validated and name_changes_disabled(realm), # password_auth_enabled is normally set via our context processor, # but for the registration form, there is no logged in user yet, so # we have to set it here. 'creating_new_team': realm_creation, 'realms_have_subdomains': settings.REALMS_HAVE_SUBDOMAINS, 'password_auth_enabled': password_auth_enabled(realm), }, request=request)
def accounts_register( request: HttpRequest, key: str = REQ(default=""), timezone: str = REQ(default="", converter=to_timezone_or_empty), from_confirmation: Optional[str] = REQ(default=None), form_full_name: Optional[str] = REQ("full_name", default=None), source_realm_id: Optional[int] = REQ( default=None, converter=to_converted_or_fallback(to_non_negative_int, None) ), ) -> HttpResponse: try: prereg_user = check_prereg_key(request, key) except ConfirmationKeyException as e: return render_confirmation_key_error(request, e) email = prereg_user.email realm_creation = prereg_user.realm_creation password_required = prereg_user.password_required role = prereg_user.invited_as if realm_creation: role = UserProfile.ROLE_REALM_OWNER try: validators.validate_email(email) except ValidationError: return render(request, "zerver/invalid_email.html", context={"invalid_email": True}) if realm_creation: # For creating a new realm, there is no existing realm or domain realm = None else: assert prereg_user.realm is not None if get_subdomain(request) != prereg_user.realm.string_id: return render_confirmation_key_error( request, ConfirmationKeyException(ConfirmationKeyException.DOES_NOT_EXIST) ) realm = prereg_user.realm try: email_allowed_for_realm(email, realm) except DomainNotAllowedForRealmError: return render( request, "zerver/invalid_email.html", context={"realm_name": realm.name, "closed_domain": True}, ) except DisposableEmailError: return render( request, "zerver/invalid_email.html", context={"realm_name": realm.name, "disposable_emails_not_allowed": True}, ) except EmailContainsPlusError: return render( request, "zerver/invalid_email.html", context={"realm_name": realm.name, "email_contains_plus": True}, ) if realm.deactivated: # The user is trying to register for a deactivated realm. Advise them to # contact support. return redirect_to_deactivation_notice() try: validate_email_not_already_in_realm(realm, email) except ValidationError: return redirect_to_email_login_url(email) if settings.BILLING_ENABLED: try: check_spare_licenses_available_for_registering_new_user(realm, email) except LicenseLimitError: return render(request, "zerver/no_spare_licenses.html") name_validated = False require_ldap_password = False if from_confirmation: try: del request.session["authenticated_full_name"] except KeyError: pass ldap_full_name = None if settings.POPULATE_PROFILE_VIA_LDAP: # If the user can be found in LDAP, we'll take the full name from the directory, # and further down create a form pre-filled with it. for backend in get_backends(): if isinstance(backend, LDAPBackend): try: ldap_username = backend.django_to_ldap_username(email) except ZulipLDAPExceptionNoMatchingLDAPUser: logging.warning("New account email %s could not be found in LDAP", email) break # Note that this `ldap_user` object is not a # `ZulipLDAPUser` with a `Realm` attached, so # calling `.populate_user()` on it will crash. # This is OK, since we're just accessing this user # to extract its name. # # TODO: We should potentially be accessing this # user to sync its initial avatar and custom # profile fields as well, if we indeed end up # creating a user account through this flow, # rather than waiting until `manage.py # sync_ldap_user_data` runs to populate it. ldap_user = _LDAPUser(backend, ldap_username) try: ldap_full_name = backend.get_mapped_name(ldap_user) except TypeError: break # Check whether this is ZulipLDAPAuthBackend, # which is responsible for authentication and # requires that LDAP accounts enter their LDAP # password to register, or ZulipLDAPUserPopulator, # which just populates UserProfile fields (no auth). require_ldap_password = isinstance(backend, ZulipLDAPAuthBackend) break if ldap_full_name: # We don't use initial= here, because if the form is # complete (that is, no additional fields need to be # filled out by the user) we want the form to validate, # so they can be directly registered without having to # go through this interstitial. form = RegistrationForm({"full_name": ldap_full_name}, realm_creation=realm_creation) request.session["authenticated_full_name"] = ldap_full_name name_validated = True elif realm is not None and realm.is_zephyr_mirror_realm: # For MIT users, we can get an authoritative name from Hesiod. # Technically we should check that this is actually an MIT # realm, but we can cross that bridge if we ever get a non-MIT # zephyr mirroring realm. hesiod_name = compute_mit_user_fullname(email) form = RegistrationForm( initial={"full_name": hesiod_name if "@" not in hesiod_name else ""}, realm_creation=realm_creation, ) name_validated = True elif prereg_user.full_name: if prereg_user.full_name_validated: request.session["authenticated_full_name"] = prereg_user.full_name name_validated = True form = RegistrationForm( {"full_name": prereg_user.full_name}, realm_creation=realm_creation ) else: form = RegistrationForm( initial={"full_name": prereg_user.full_name}, realm_creation=realm_creation ) elif form_full_name is not None: form = RegistrationForm( initial={"full_name": form_full_name}, realm_creation=realm_creation, ) else: form = RegistrationForm(realm_creation=realm_creation) else: postdata = request.POST.copy() if name_changes_disabled(realm): # If we populate profile information via LDAP and we have a # verified name from you on file, use that. Otherwise, fall # back to the full name in the request. try: postdata.update(full_name=request.session["authenticated_full_name"]) name_validated = True except KeyError: pass form = RegistrationForm(postdata, realm_creation=realm_creation) if not (password_auth_enabled(realm) and password_required): form["password"].field.required = False if form.is_valid(): if password_auth_enabled(realm) and form["password"].field.required: password = form.cleaned_data["password"] else: # If the user wasn't prompted for a password when # completing the authentication form (because they're # signing up with SSO and no password is required), set # the password field to `None` (Which causes Django to # create an unusable password). password = None if realm_creation: string_id = form.cleaned_data["realm_subdomain"] realm_name = form.cleaned_data["realm_name"] realm_type = form.cleaned_data["realm_type"] is_demo_org = form.cleaned_data["is_demo_organization"] realm = do_create_realm( string_id, realm_name, org_type=realm_type, is_demo_organization=is_demo_org ) setup_realm_internal_bots(realm) assert realm is not None full_name = form.cleaned_data["full_name"] enable_marketing_emails = form.cleaned_data["enable_marketing_emails"] default_stream_group_names = request.POST.getlist("default_stream_group") default_stream_groups = lookup_default_stream_groups(default_stream_group_names, realm) if source_realm_id is not None: # Non-integer realm_id values like "string" are treated # like the "Do not import" value of "". source_profile: Optional[UserProfile] = get_source_profile(email, source_realm_id) else: source_profile = None if not realm_creation: try: existing_user_profile: Optional[UserProfile] = get_user_by_delivery_email( email, realm ) except UserProfile.DoesNotExist: existing_user_profile = None else: existing_user_profile = None user_profile: Optional[UserProfile] = None return_data: Dict[str, bool] = {} if ldap_auth_enabled(realm): # If the user was authenticated using an external SSO # mechanism like Google or GitHub auth, then authentication # will have already been done before creating the # PreregistrationUser object with password_required=False, and # so we don't need to worry about passwords. # # If instead the realm is using EmailAuthBackend, we will # set their password above. # # But if the realm is using LDAPAuthBackend, we need to verify # their LDAP password (which will, as a side effect, create # the user account) here using authenticate. # pregeg_user.realm_creation carries the information about whether # we're in realm creation mode, and the ldap flow will handle # that and create the user with the appropriate parameters. user_profile = authenticate( request=request, username=email, password=password, realm=realm, prereg_user=prereg_user, return_data=return_data, ) if user_profile is None: can_use_different_backend = email_auth_enabled(realm) or ( len(get_external_method_dicts(realm)) > 0 ) if settings.LDAP_APPEND_DOMAIN: # In LDAP_APPEND_DOMAIN configurations, we don't allow making a non-LDAP account # if the email matches the ldap domain. can_use_different_backend = can_use_different_backend and ( not email_belongs_to_ldap(realm, email) ) if return_data.get("no_matching_ldap_user") and can_use_different_backend: # If both the LDAP and Email or Social auth backends are # enabled, and there's no matching user in the LDAP # directory then the intent is to create a user in the # realm with their email outside the LDAP organization # (with e.g. a password stored in the Zulip database, # not LDAP). So we fall through and create the new # account. pass else: # TODO: This probably isn't going to give a # user-friendly error message, but it doesn't # particularly matter, because the registration form # is hidden for most users. view_url = reverse("login") query = urlencode({"email": email}) redirect_url = append_url_query_string(view_url, query) return HttpResponseRedirect(redirect_url) elif not realm_creation: # Since we'll have created a user, we now just log them in. return login_and_go_to_home(request, user_profile) else: # With realm_creation=True, we're going to return further down, # after finishing up the creation process. pass if existing_user_profile is not None and existing_user_profile.is_mirror_dummy: user_profile = existing_user_profile do_activate_mirror_dummy_user(user_profile, acting_user=user_profile) do_change_password(user_profile, password) do_change_full_name(user_profile, full_name, user_profile) do_change_user_setting(user_profile, "timezone", timezone, acting_user=user_profile) # TODO: When we clean up the `do_activate_mirror_dummy_user` code path, # make it respect invited_as_admin / is_realm_admin. if user_profile is None: user_profile = do_create_user( email, password, realm, full_name, prereg_user=prereg_user, role=role, tos_version=settings.TOS_VERSION, timezone=timezone, default_stream_groups=default_stream_groups, source_profile=source_profile, realm_creation=realm_creation, acting_user=None, enable_marketing_emails=enable_marketing_emails, ) if realm_creation: assert realm.signup_notifications_stream is not None bulk_add_subscriptions( realm, [realm.signup_notifications_stream], [user_profile], acting_user=None ) send_initial_realm_messages(realm) # Because for realm creation, registration happens on the # root domain, we need to log them into the subdomain for # their new realm. return redirect_and_log_into_subdomain( ExternalAuthResult(user_profile=user_profile, data_dict={"is_realm_creation": True}) ) # This dummy_backend check below confirms the user is # authenticating to the correct subdomain. auth_result = authenticate( username=user_profile.delivery_email, realm=realm, return_data=return_data, use_dummy_backend=True, ) if return_data.get("invalid_subdomain"): # By construction, this should never happen. logging.error( "Subdomain mismatch in registration %s: %s", realm.subdomain, user_profile.delivery_email, ) return redirect("/") return login_and_go_to_home(request, auth_result) return render( request, "zerver/register.html", context={ "form": form, "email": email, "key": key, "full_name": request.session.get("authenticated_full_name", None), "lock_name": name_validated and name_changes_disabled(realm), # password_auth_enabled is normally set via our context processor, # but for the registration form, there is no logged in user yet, so # we have to set it here. "creating_new_team": realm_creation, "password_required": password_auth_enabled(realm) and password_required, "require_ldap_password": require_ldap_password, "password_auth_enabled": password_auth_enabled(realm), "root_domain_available": is_root_domain_available(), "default_stream_groups": [] if realm is None else get_default_stream_groups(realm), "accounts": get_accounts_for_email(email), "MAX_REALM_NAME_LENGTH": str(Realm.MAX_REALM_NAME_LENGTH), "MAX_NAME_LENGTH": str(UserProfile.MAX_NAME_LENGTH), "MAX_PASSWORD_LENGTH": str(form.MAX_PASSWORD_LENGTH), "MAX_REALM_SUBDOMAIN_LENGTH": str(Realm.MAX_REALM_SUBDOMAIN_LENGTH), "sorted_realm_types": sorted( Realm.ORG_TYPES.values(), key=lambda d: d["display_order"] ), }, )
def ldap_to_django_username(self, username): # force uid if someone give a email return _LDAPUser(self, username=username).attrs['uid'][0]
def handle(self, *args, **options): from django_auth_ldap import backend with disable_django_caching(backend): with disable_permission_checks(User): with disable_permission_checks(UserProfile): with disable_permission_checks(Group): with disable_permission_checks(UserStorageLimit): with disable_permission_checks( NotificationConfiguration): users_processed = 0 start_time = time.time() self.ldap_backend = LDAPBackend() user = _LDAPUser(backend=self.ldap_backend, username='') ldap_connection = user.connection # settings.AUTH_LDAP_USER_SEARCH.base_dn # settings.AUTH_LDAP_USER_SEARCH.filterstr lc = create_controls(PAGE_SIZE) while True: # Send search request try: # If you leave out the ATTRLIST it'll return all attributes # which you have permissions to access. You may want to adjust # the scope level as well (perhaps "ldap.SCOPE_SUBTREE", but # it can reduce performance if you don't need it). msgid = ldap_connection.search_ext( settings.AUTH_LDAP_USER_SEARCH. base_dn, ldap.SCOPE_SUBTREE, 'uid=*', serverctrls=[lc]) except ldap.LDAPError as e: print('LDAP search failed: %s' % e) exit() # Pull the results from the search request try: rtype, rdata, rmsgid, serverctrls = ldap_connection.result3( msgid) except ldap.LDAPError as e: print( 'Could not pull LDAP results: %s' % e) exit() rdata = settings.AUTH_LDAP_USER_SEARCH._process_results( rdata) # Each "rdata" is a tuple of the form (dn, attrs), where dn is # a string containing the DN (distinguished name) of the entry, # and attrs is a dictionary containing the attributes associated # with the entry. The keys of attrs are strings, and the associated # values are lists of strings. raw_processing_time_start = time.time() for dn, attrs in rdata: self.process_entry(dn, attrs) users_processed += 1 raw_processing_time_end = time.time() # Get cookie for next request pctrls = get_pctrls(serverctrls) if not pctrls: print( 'Warning: Server ignores RFC 2696 control.' ) break # Ok, we did find the page control, yank the cookie from it and # insert it into the control for our next search. If however there # is no cookie, we are done! cookie = set_cookie(lc, pctrls, PAGE_SIZE) if not cookie: break # write a message with some statistics print( "Processed {} users in {} seconds (avg {} users per second, raw processing " "time {} seconds)".format( users_processed, time.time() - start_time, users_processed / (time.time() - start_time), raw_processing_time_end - raw_processing_time_start))
def accounts_register(request): # type: (HttpRequest) -> HttpResponse key = request.POST['key'] confirmation = Confirmation.objects.get(confirmation_key=key) prereg_user = confirmation.content_object email = prereg_user.email realm_creation = prereg_user.realm_creation password_required = prereg_user.password_required validators.validate_email(email) if prereg_user.referred_by: # If someone invited you, you are joining their realm regardless # of your e-mail address. realm = prereg_user.referred_by.realm elif realm_creation: # For creating a new realm, there is no existing realm or domain realm = None else: realm = get_realm(get_subdomain(request)) if realm and not email_allowed_for_realm(email, realm): return render(request, "zerver/closed_realm.html", context={"closed_domain_name": realm.name}) if realm and realm.deactivated: # The user is trying to register for a deactivated realm. Advise them to # contact support. return redirect_to_deactivation_notice() try: validate_email_for_realm(realm, email) except ValidationError: return HttpResponseRedirect( reverse('django.contrib.auth.views.login') + '?email=' + urllib.parse.quote_plus(email)) name_validated = False full_name = None if request.POST.get('from_confirmation'): try: del request.session['authenticated_full_name'] except KeyError: pass if realm is not None and realm.is_zephyr_mirror_realm: # For MIT users, we can get an authoritative name from Hesiod. # Technically we should check that this is actually an MIT # realm, but we can cross that bridge if we ever get a non-MIT # zephyr mirroring realm. hesiod_name = compute_mit_user_fullname(email) form = RegistrationForm(initial={ 'full_name': hesiod_name if "@" not in hesiod_name else "" }, realm_creation=realm_creation) name_validated = True elif settings.POPULATE_PROFILE_VIA_LDAP: for backend in get_backends(): if isinstance(backend, LDAPBackend): ldap_attrs = _LDAPUser( backend, backend.django_to_ldap_username(email)).attrs try: ldap_full_name = ldap_attrs[ settings.AUTH_LDAP_USER_ATTR_MAP['full_name']][0] request.session[ 'authenticated_full_name'] = ldap_full_name name_validated = True # We don't use initial= here, because if the form is # complete (that is, no additional fields need to be # filled out by the user) we want the form to validate, # so they can be directly registered without having to # go through this interstitial. form = RegistrationForm({'full_name': ldap_full_name}, realm_creation=realm_creation) # FIXME: This will result in the user getting # validation errors if they have to enter a password. # Not relevant for ONLY_SSO, though. break except TypeError: # Let the user fill out a name and/or try another backend form = RegistrationForm(realm_creation=realm_creation) elif 'full_name' in request.POST: form = RegistrationForm( initial={'full_name': request.POST.get('full_name')}, realm_creation=realm_creation) else: form = RegistrationForm(realm_creation=realm_creation) else: postdata = request.POST.copy() if name_changes_disabled(realm): # If we populate profile information via LDAP and we have a # verified name from you on file, use that. Otherwise, fall # back to the full name in the request. try: postdata.update( {'full_name': request.session['authenticated_full_name']}) name_validated = True except KeyError: pass form = RegistrationForm(postdata, realm_creation=realm_creation) if not (password_auth_enabled(realm) and password_required): form['password'].field.required = False if form.is_valid(): if password_auth_enabled(realm): password = form.cleaned_data['password'] else: # SSO users don't need no passwords password = None if realm_creation: string_id = form.cleaned_data['realm_subdomain'] realm_name = form.cleaned_data['realm_name'] realm = do_create_realm(string_id, realm_name) setup_initial_streams(realm) assert (realm is not None) full_name = form.cleaned_data['full_name'] short_name = email_to_username(email) default_stream_group_names = request.POST.getlist( 'default_stream_group') default_stream_groups = lookup_default_stream_groups( default_stream_group_names, realm) timezone = u"" if 'timezone' in request.POST and request.POST[ 'timezone'] in get_all_timezones(): timezone = request.POST['timezone'] try: existing_user_profile = get_user_profile_by_email(email) except UserProfile.DoesNotExist: existing_user_profile = None return_data = {} # type: Dict[str, bool] if ldap_auth_enabled(realm): # If the user was authenticated using an external SSO # mechanism like Google or GitHub auth, then authentication # will have already been done before creating the # PreregistrationUser object with password_required=False, and # so we don't need to worry about passwords. # # If instead the realm is using EmailAuthBackend, we will # set their password above. # # But if the realm is using LDAPAuthBackend, we need to verify # their LDAP password (which will, as a side effect, create # the user account) here using authenticate. auth_result = authenticate(request, username=email, password=password, realm_subdomain=realm.subdomain, return_data=return_data) if auth_result is None: # TODO: This probably isn't going to give a # user-friendly error message, but it doesn't # particularly matter, because the registration form # is hidden for most users. return HttpResponseRedirect( reverse('django.contrib.auth.views.login') + '?email=' + urllib.parse.quote_plus(email)) # Since we'll have created a user, we now just log them in. return login_and_go_to_home(request, auth_result) elif existing_user_profile is not None and existing_user_profile.is_mirror_dummy: user_profile = existing_user_profile do_activate_user(user_profile) do_change_password(user_profile, password) do_change_full_name(user_profile, full_name, user_profile) do_set_user_display_setting(user_profile, 'timezone', timezone) else: user_profile = do_create_user( email, password, realm, full_name, short_name, prereg_user=prereg_user, is_realm_admin=realm_creation, tos_version=settings.TOS_VERSION, timezone=timezone, newsletter_data={"IP": request.META['REMOTE_ADDR']}, default_stream_groups=default_stream_groups) if realm_creation: setup_initial_private_stream(user_profile) send_initial_realm_messages(realm) if realm_creation: # Because for realm creation, registration happens on the # root domain, we need to log them into the subdomain for # their new realm. return redirect_and_log_into_subdomain(realm, full_name, email) # This dummy_backend check below confirms the user is # authenticating to the correct subdomain. auth_result = authenticate(username=user_profile.email, realm_subdomain=realm.subdomain, return_data=return_data, use_dummy_backend=True) if return_data.get('invalid_subdomain'): # By construction, this should never happen. logging.error("Subdomain mismatch in registration %s: %s" % ( realm.subdomain, user_profile.email, )) return redirect('/') return login_and_go_to_home(request, auth_result) return render( request, 'zerver/register.html', context={ 'form': form, 'email': email, 'key': key, 'full_name': request.session.get('authenticated_full_name', None), 'lock_name': name_validated and name_changes_disabled(realm), # password_auth_enabled is normally set via our context processor, # but for the registration form, there is no logged in user yet, so # we have to set it here. 'creating_new_team': realm_creation, 'password_required': password_auth_enabled(realm) and password_required, 'password_auth_enabled': password_auth_enabled(realm), 'root_domain_available': is_root_domain_available(), 'default_stream_groups': get_default_stream_groups(realm), 'MAX_REALM_NAME_LENGTH': str(Realm.MAX_REALM_NAME_LENGTH), 'MAX_NAME_LENGTH': str(UserProfile.MAX_NAME_LENGTH), 'MAX_PASSWORD_LENGTH': str(form.MAX_PASSWORD_LENGTH), 'MAX_REALM_SUBDOMAIN_LENGTH': str(Realm.MAX_REALM_SUBDOMAIN_LENGTH) })
def _create_or_update_user(self, user_dn, ldap_dict): username = ldap_dict[settings.AUTH_LDAP_USER_USERNAME_ATTR][0] ldap_user = _LDAPUser(self.backend, username=username.lower()) ldap_user._user_dn = user_dn ldap_user._user_attrs = ldap_dict ldap_user.populate_user()
def accounts_register(request: HttpRequest) -> HttpResponse: key = request.POST['key'] confirmation = Confirmation.objects.get(confirmation_key=key) prereg_user = confirmation.content_object email = prereg_user.email realm_creation = prereg_user.realm_creation password_required = prereg_user.password_required is_realm_admin = prereg_user.invited_as == PreregistrationUser.INVITE_AS[ 'REALM_ADMIN'] or realm_creation is_guest = prereg_user.invited_as == PreregistrationUser.INVITE_AS[ 'GUEST_USER'] try: validators.validate_email(email) except ValidationError: return render(request, "zerver/invalid_email.html", context={"invalid_email": True}) if realm_creation: # For creating a new realm, there is no existing realm or domain realm = None else: if get_subdomain(request) != prereg_user.realm.string_id: return render_confirmation_key_error( request, ConfirmationKeyException( ConfirmationKeyException.DOES_NOT_EXIST)) realm = prereg_user.realm try: email_allowed_for_realm(email, realm) except DomainNotAllowedForRealmError: return render(request, "zerver/invalid_email.html", context={ "realm_name": realm.name, "closed_domain": True }) except DisposableEmailError: return render(request, "zerver/invalid_email.html", context={ "realm_name": realm.name, "disposable_emails_not_allowed": True }) except EmailContainsPlusError: return render(request, "zerver/invalid_email.html", context={ "realm_name": realm.name, "email_contains_plus": True }) if realm.deactivated: # The user is trying to register for a deactivated realm. Advise them to # contact support. return redirect_to_deactivation_notice() try: validate_email_for_realm(realm, email) except ValidationError: return HttpResponseRedirect( reverse('django.contrib.auth.views.login') + '?email=' + urllib.parse.quote_plus(email)) name_validated = False full_name = None require_ldap_password = False if request.POST.get('from_confirmation'): try: del request.session['authenticated_full_name'] except KeyError: pass ldap_full_name = None if settings.POPULATE_PROFILE_VIA_LDAP: # If the user can be found in LDAP, we'll take the full name from the directory, # and further down create a form pre-filled with it. for backend in get_backends(): if isinstance(backend, LDAPBackend): try: ldap_username = backend.django_to_ldap_username(email) except ZulipLDAPExceptionNoMatchingLDAPUser: logging.warning( "New account email %s could not be found in LDAP" % (email, )) break # Note that this `ldap_user` object is not a # `ZulipLDAPUser` with a `Realm` attached, so # calling `.populate_user()` on it will crash. # This is OK, since we're just accessing this user # to extract its name. # # TODO: We should potentially be accessing this # user to sync its initial avatar and custom # profile fields as well, if we indeed end up # creating a user account through this flow, # rather than waiting until `manage.py # sync_ldap_user_data` runs to populate it. ldap_user = _LDAPUser(backend, ldap_username) try: ldap_full_name, _ = backend.get_mapped_name(ldap_user) except TypeError: break # Check whether this is ZulipLDAPAuthBackend, # which is responsible for authentication and # requires that LDAP accounts enter their LDAP # password to register, or ZulipLDAPUserPopulator, # which just populates UserProfile fields (no auth). require_ldap_password = isinstance(backend, ZulipLDAPAuthBackend) break if ldap_full_name: # We don't use initial= here, because if the form is # complete (that is, no additional fields need to be # filled out by the user) we want the form to validate, # so they can be directly registered without having to # go through this interstitial. form = RegistrationForm({'full_name': ldap_full_name}, realm_creation=realm_creation) request.session['authenticated_full_name'] = ldap_full_name name_validated = True elif realm is not None and realm.is_zephyr_mirror_realm: # For MIT users, we can get an authoritative name from Hesiod. # Technically we should check that this is actually an MIT # realm, but we can cross that bridge if we ever get a non-MIT # zephyr mirroring realm. hesiod_name = compute_mit_user_fullname(email) form = RegistrationForm(initial={ 'full_name': hesiod_name if "@" not in hesiod_name else "" }, realm_creation=realm_creation) name_validated = True elif prereg_user.full_name: if prereg_user.full_name_validated: request.session[ 'authenticated_full_name'] = prereg_user.full_name name_validated = True form = RegistrationForm({'full_name': prereg_user.full_name}, realm_creation=realm_creation) else: form = RegistrationForm( initial={'full_name': prereg_user.full_name}, realm_creation=realm_creation) elif 'full_name' in request.POST: form = RegistrationForm( initial={'full_name': request.POST.get('full_name')}, realm_creation=realm_creation) else: form = RegistrationForm(realm_creation=realm_creation) else: postdata = request.POST.copy() if name_changes_disabled(realm): # If we populate profile information via LDAP and we have a # verified name from you on file, use that. Otherwise, fall # back to the full name in the request. try: postdata.update( {'full_name': request.session['authenticated_full_name']}) name_validated = True except KeyError: pass form = RegistrationForm(postdata, realm_creation=realm_creation) if not (password_auth_enabled(realm) and password_required): form['password'].field.required = False if form.is_valid(): if password_auth_enabled(realm) and form['password'].field.required: password = form.cleaned_data['password'] else: # If the user wasn't prompted for a password when # completing the authentication form (because they're # signing up with SSO and no password is required), set # the password field to `None` (Which causes Django to # create an unusable password). password = None if realm_creation: string_id = form.cleaned_data['realm_subdomain'] realm_name = form.cleaned_data['realm_name'] realm = do_create_realm(string_id, realm_name) setup_realm_internal_bots(realm) assert (realm is not None) full_name = form.cleaned_data['full_name'] short_name = email_to_username(email) default_stream_group_names = request.POST.getlist( 'default_stream_group') default_stream_groups = lookup_default_stream_groups( default_stream_group_names, realm) timezone = "" if 'timezone' in request.POST and request.POST[ 'timezone'] in get_all_timezones(): timezone = request.POST['timezone'] if 'source_realm' in request.POST and request.POST[ "source_realm"] != "on": source_profile = get_source_profile(email, request.POST["source_realm"]) else: source_profile = None if not realm_creation: try: existing_user_profile = get_user_by_delivery_email( email, realm) # type: Optional[UserProfile] except UserProfile.DoesNotExist: existing_user_profile = None else: existing_user_profile = None user_profile = None # type: Optional[UserProfile] return_data = {} # type: Dict[str, bool] if ldap_auth_enabled(realm): # If the user was authenticated using an external SSO # mechanism like Google or GitHub auth, then authentication # will have already been done before creating the # PreregistrationUser object with password_required=False, and # so we don't need to worry about passwords. # # If instead the realm is using EmailAuthBackend, we will # set their password above. # # But if the realm is using LDAPAuthBackend, we need to verify # their LDAP password (which will, as a side effect, create # the user account) here using authenticate. # pregeg_user.realm_creation carries the information about whether # we're in realm creation mode, and the ldap flow will handle # that and create the user with the appropriate parameters. user_profile = authenticate(request, username=email, password=password, realm=realm, prereg_user=prereg_user, return_data=return_data) if user_profile is None: can_use_different_backend = email_auth_enabled( realm) or any_social_backend_enabled(realm) if settings.LDAP_APPEND_DOMAIN: # In LDAP_APPEND_DOMAIN configurations, we don't allow making a non-ldap account # if the email matches the ldap domain. can_use_different_backend = can_use_different_backend and ( not email_belongs_to_ldap(realm, email)) if return_data.get( "no_matching_ldap_user") and can_use_different_backend: # If both the LDAP and Email or Social auth backends are # enabled, and there's no matching user in the LDAP # directory then the intent is to create a user in the # realm with their email outside the LDAP organization # (with e.g. a password stored in the Zulip database, # not LDAP). So we fall through and create the new # account. pass else: # TODO: This probably isn't going to give a # user-friendly error message, but it doesn't # particularly matter, because the registration form # is hidden for most users. return HttpResponseRedirect( reverse('django.contrib.auth.views.login') + '?email=' + urllib.parse.quote_plus(email)) elif not realm_creation: # Since we'll have created a user, we now just log them in. return login_and_go_to_home(request, user_profile) else: # With realm_creation=True, we're going to return further down, # after finishing up the creation process. pass if existing_user_profile is not None and existing_user_profile.is_mirror_dummy: user_profile = existing_user_profile do_activate_user(user_profile) do_change_password(user_profile, password) do_change_full_name(user_profile, full_name, user_profile) do_set_user_display_setting(user_profile, 'timezone', timezone) # TODO: When we clean up the `do_activate_user` code path, # make it respect invited_as_admin / is_realm_admin. if user_profile is None: user_profile = do_create_user( email, password, realm, full_name, short_name, prereg_user=prereg_user, is_realm_admin=is_realm_admin, is_guest=is_guest, tos_version=settings.TOS_VERSION, timezone=timezone, newsletter_data={"IP": request.META['REMOTE_ADDR']}, default_stream_groups=default_stream_groups, source_profile=source_profile, realm_creation=realm_creation) if realm_creation: bulk_add_subscriptions([realm.signup_notifications_stream], [user_profile]) send_initial_realm_messages(realm) # Because for realm creation, registration happens on the # root domain, we need to log them into the subdomain for # their new realm. return redirect_and_log_into_subdomain(realm, full_name, email) # This dummy_backend check below confirms the user is # authenticating to the correct subdomain. auth_result = authenticate(username=user_profile.delivery_email, realm=realm, return_data=return_data, use_dummy_backend=True) if return_data.get('invalid_subdomain'): # By construction, this should never happen. logging.error("Subdomain mismatch in registration %s: %s" % ( realm.subdomain, user_profile.delivery_email, )) return redirect('/') return login_and_go_to_home(request, auth_result) return render( request, 'zerver/register.html', context={ 'form': form, 'email': email, 'key': key, 'full_name': request.session.get('authenticated_full_name', None), 'lock_name': name_validated and name_changes_disabled(realm), # password_auth_enabled is normally set via our context processor, # but for the registration form, there is no logged in user yet, so # we have to set it here. 'creating_new_team': realm_creation, 'password_required': password_auth_enabled(realm) and password_required, 'require_ldap_password': require_ldap_password, 'password_auth_enabled': password_auth_enabled(realm), 'root_domain_available': is_root_domain_available(), 'default_stream_groups': get_default_stream_groups(realm), 'accounts': get_accounts_for_email(email), 'MAX_REALM_NAME_LENGTH': str(Realm.MAX_REALM_NAME_LENGTH), 'MAX_NAME_LENGTH': str(UserProfile.MAX_NAME_LENGTH), 'MAX_PASSWORD_LENGTH': str(form.MAX_PASSWORD_LENGTH), 'MAX_REALM_SUBDOMAIN_LENGTH': str(Realm.MAX_REALM_SUBDOMAIN_LENGTH) })