def handle(self, *args, **options): # type: (*Any, **str) -> None realm = get_realm(options["string_id"]) if options["op"] == "show": print("Aliases for %s:" % (realm.domain,)) for alias in get_realm_aliases(realm): print(alias["domain"]) sys.exit(0) domain = options['alias'].strip().lower() try: validate_domain(domain) except ValidationError as e: print(e.messages[0]) sys.exit(1) if options["op"] == "add": if not can_add_alias(domain): print("A Realm already exists for this domain, cannot add it as an alias for another realm!") sys.exit(1) RealmAlias.objects.create(realm=realm, domain=domain) sys.exit(0) elif options["op"] == "remove": RealmAlias.objects.get(realm=realm, domain=domain).delete() sys.exit(0) else: self.print_help("./manage.py", "realm_alias") sys.exit(1)
def handle(self, *args, **options): # type: (*Any, **str) -> None realm = get_realm(options["string_id"]) if options["op"] == "show": print("Aliases for %s:" % (realm.domain, )) for alias in get_realm_aliases(realm): print(alias["domain"]) sys.exit(0) domain = options['alias'].strip().lower() try: validate_domain(domain) except ValidationError as e: print(e.messages[0]) sys.exit(1) if options["op"] == "add": if not can_add_alias(domain): print( "A Realm already exists for this domain, cannot add it as an alias for another realm!" ) sys.exit(1) RealmAlias.objects.create(realm=realm, domain=domain) sys.exit(0) elif options["op"] == "remove": RealmAlias.objects.get(realm=realm, domain=domain).delete() sys.exit(0) else: self.print_help("./manage.py", "realm_alias") sys.exit(1)
def handle(self, *args: Any, **options: str) -> None: realm = self.get_realm(options) assert realm is not None # Should be ensured by parser if options["op"] == "show": print(f"Domains for {realm.string_id}:") for realm_domain in get_realm_domains(realm): if realm_domain["allow_subdomains"]: print(realm_domain["domain"] + " (subdomains allowed)") else: print(realm_domain["domain"] + " (subdomains not allowed)") sys.exit(0) domain = options['domain'].strip().lower() try: validate_domain(domain) except ValidationError as e: raise CommandError(e.messages[0]) if options["op"] == "add": try: RealmDomain.objects.create(realm=realm, domain=domain, allow_subdomains=options["allow_subdomains"]) sys.exit(0) except IntegrityError: raise CommandError(f"The domain {domain} is already a part " "of your organization.") elif options["op"] == "remove": try: RealmDomain.objects.get(realm=realm, domain=domain).delete() sys.exit(0) except RealmDomain.DoesNotExist: raise CommandError("No such entry found!") else: self.print_help("./manage.py", "realm_domain") raise CommandError
def test_validate_domain(self) -> None: invalid_domains = ['', 'test', 't.', 'test.', '.com', '-test', 'test...com', 'test-', 'test_domain.com', 'test.-domain.com', 'a' * 255 + ".com"] for domain in invalid_domains: with self.assertRaises(ValidationError): validate_domain(domain) valid_domains = ['acme.com', 'x-x.y.3.z'] for domain in valid_domains: validate_domain(domain)
def create_realm_domain(request: HttpRequest, user_profile: UserProfile, domain: Text=REQ(validator=check_string), allow_subdomains: bool=REQ(validator=check_bool)) -> HttpResponse: domain = domain.strip().lower() try: validate_domain(domain) except ValidationError as e: return json_error(_('Invalid domain: {}').format(e.messages[0])) if RealmDomain.objects.filter(realm=user_profile.realm, domain=domain).exists(): return json_error(_("The domain %(domain)s is already a part of your organization.") % {'domain': domain}) realm_domain = do_add_realm_domain(user_profile.realm, domain, allow_subdomains) return json_success({'new_domain': [realm_domain.id, realm_domain.domain]})
def create_alias(request, user_profile, domain=REQ(validator=check_string)): # type: (HttpRequest, UserProfile, Text) -> (HttpResponse) domain = domain.strip().lower() try: validate_domain(domain) except ValidationError as e: return json_error(_('Invalid domain: {}').format(e.messages[0])) if RealmAlias.objects.filter(realm=user_profile.realm, domain=domain).exists(): return json_error(_("The domain %(domain)s is already a part of your organization.") % {'domain': domain}) if not can_add_alias(domain): return json_error(_("The domain %(domain)s belongs to another organization.") % {'domain': domain}) alias = do_add_realm_alias(user_profile.realm, domain) return json_success({'new_domain': [alias.id, alias.domain]})
def create_realm_domain(request, user_profile, domain=REQ(validator=check_string), allow_subdomains=REQ(validator=check_bool)): # type: (HttpRequest, UserProfile, Text, bool) -> (HttpResponse) domain = domain.strip().lower() try: validate_domain(domain) except ValidationError as e: return json_error(_('Invalid domain: {}').format(e.messages[0])) if RealmDomain.objects.filter(realm=user_profile.realm, domain=domain).exists(): return json_error(_("The domain %(domain)s is already a part of your organization.") % {'domain': domain}) if not can_add_realm_domain(domain): return json_error(_("The domain %(domain)s belongs to another organization.") % {'domain': domain}) realm_domain = do_add_realm_domain(user_profile.realm, domain, allow_subdomains) return json_success({'new_domain': [realm_domain.id, realm_domain.domain]})
def create_realm_domain(request: HttpRequest, user_profile: UserProfile, domain: str=REQ(validator=check_string), allow_subdomains: bool=REQ(validator=check_bool)) -> HttpResponse: domain = domain.strip().lower() try: validate_domain(domain) except ValidationError as e: return json_error(_('Invalid domain: {}').format(e.messages[0])) if RealmDomain.objects.filter(realm=user_profile.realm, domain=domain).exists(): return json_error(_("The domain {domain} is already" " a part of your organization.").format(domain=domain)) realm_domain = do_add_realm_domain(user_profile.realm, domain, allow_subdomains) return json_success({'new_domain': [realm_domain.id, realm_domain.domain]})
def handle(self, *args, **options): # type: (*Any, **Any) -> None string_id = options["string_id"] name = options["name"] domain = options["domain"].lower() if not name or not string_id: print("\033[1;31mPlease provide a name and string_id.\033[0m\n", file=sys.stderr) self.print_help("./manage.py", "create_realm") exit(1) if options["deployment_id"] is not None and not settings.ZILENCER_ENABLED: print("\033[1;31mExternal deployments are not supported on voyager deployments.\033[0m\n", file=sys.stderr) exit(1) try: validate_domain(domain) except ValidationError as e: print(e.messages[0]) sys.exit(1) if get_realm(string_id) is not None: raise ValueError("string_id taken. Please choose another one.") realm, created = do_create_realm(string_id, name, org_type=options["org_type"]) if created: print(string_id, "created.") if domain: RealmAlias.objects.create(realm=realm, domain=domain) print("RealmAlias %s created for realm %s" % (domain, string_id)) if options["deployment_id"] is not None: deployment = Deployment.objects.get(id=options["deployment_id"]) deployment.realms.add(realm) deployment.save() print("Added to deployment", str(deployment.id)) elif settings.PRODUCTION and settings.ZILENCER_ENABLED: deployment = Deployment.objects.get(base_site_url="https://zulip.com/") deployment.realms.add(realm) deployment.save() # In the else case, we are not using the Deployments feature. stream_dict = { "social": {"description": "For socializing", "invite_only": False}, "engineering": {"description": "For engineering", "invite_only": False} } # type: Dict[Text, Dict[Text, Any]] set_default_streams(realm, stream_dict) print("\033[1;36mDefault streams set to social,engineering,zulip!\033[0m") else: print(string_id, "already exists.")
def handle(self, *args, **options): # type: (*Any, **Any) -> None string_id = options["string_id"] name = options["name"] domain = options["domain"].lower() if not name or not string_id: print("\033[1;31mPlease provide a name and string_id.\033[0m\n", file=sys.stderr) self.print_help("./manage.py", "create_realm") exit(1) try: validate_domain(domain) except ValidationError as e: print(e.messages[0]) sys.exit(1) if get_realm(string_id) is not None: raise ValueError("string_id taken. Please choose another one.") realm, created = do_create_realm(string_id, name, org_type=options["org_type"]) if created: print(string_id, "created.") if domain: RealmAlias.objects.create(realm=realm, domain=domain) print("RealmAlias %s created for realm %s" % (domain, string_id)) stream_dict = { "social": { "description": "For socializing", "invite_only": False }, "engineering": { "description": "For engineering", "invite_only": False } } # type: Dict[Text, Dict[Text, Any]] set_default_streams(realm, stream_dict) print( "\033[1;36mDefault streams set to social,engineering,zulip!\033[0m" ) else: print(string_id, "already exists.")
def handle(self, *args, **options): # type: (*Any, **str) -> None realm = self.get_realm(options) assert realm is not None # Should be ensured by parser if options["op"] == "show": print("Domains for %s:" % (realm.string_id, )) for realm_domain in get_realm_domains(realm): if realm_domain["allow_subdomains"]: print(realm_domain["domain"] + " (subdomains allowed)") else: print(realm_domain["domain"] + " (subdomains not allowed)") sys.exit(0) domain = options['domain'].strip().lower() try: validate_domain(domain) except ValidationError as e: print(e.messages[0]) sys.exit(1) if options["op"] == "add": try: if not can_add_realm_domain(domain): print( "The domain %(domain)s belongs to another organization." % {'domain': domain}) sys.exit(1) RealmDomain.objects.create( realm=realm, domain=domain, allow_subdomains=options["allow_subdomains"]) sys.exit(0) except IntegrityError: print( "The domain %(domain)s is already a part of your organization." % {'domain': domain}) sys.exit(1) elif options["op"] == "remove": try: RealmDomain.objects.get(realm=realm, domain=domain).delete() sys.exit(0) except RealmDomain.DoesNotExist: print("No such entry found!") sys.exit(1) else: self.print_help("./manage.py", "realm_domain") sys.exit(1)
def test_validate_domain(self) -> None: invalid_domains = [ "", "test", "t.", "test.", ".com", "-test", "test...com", "test-", "test_domain.com", "test.-domain.com", "a" * 255 + ".com", ] for domain in invalid_domains: with self.assertRaises(ValidationError): validate_domain(domain) valid_domains = ["acme.com", "x-x.y.3.z"] for domain in valid_domains: validate_domain(domain)
def handle(self, *args, **options): # type: (*Any, **str) -> None realm = get_realm(options["string_id"]) if options["op"] == "show": print("Aliases for %s:" % (realm.domain,)) for alias in get_realm_aliases(realm): if alias["allow_subdomains"]: print(alias["domain"] + " (subdomains allowed)") else: print(alias["domain"] + " (subdomains not allowed)") sys.exit(0) domain = options['alias'].strip().lower() try: validate_domain(domain) except ValidationError as e: print(e.messages[0]) sys.exit(1) if options["op"] == "add": try: if not can_add_alias(domain): print(_("The domain %(domain)s belongs to another organization.") % {'domain': domain}) sys.exit(1) RealmAlias.objects.create(realm=realm, domain=domain, allow_subdomains=options["allow_subdomains"]) sys.exit(0) except IntegrityError: print(_("The domain %(domain)s is already a part of your organization.") % {'domain': domain}) sys.exit(1) elif options["op"] == "remove": try: RealmAlias.objects.get(realm=realm, domain=domain).delete() sys.exit(0) except RealmAlias.DoesNotExist: print("No such entry found!") sys.exit(1) else: self.print_help("./manage.py", "realm_alias") sys.exit(1)
def create_realm_domain( request: HttpRequest, user_profile: UserProfile, domain: str = REQ(), allow_subdomains: bool = REQ(json_validator=check_bool), ) -> HttpResponse: domain = domain.strip().lower() try: validate_domain(domain) except ValidationError as e: raise JsonableError(_("Invalid domain: {}").format(e.messages[0])) if RealmDomain.objects.filter(realm=user_profile.realm, domain=domain).exists(): raise JsonableError( _("The domain {domain} is already a part of your organization."). format(domain=domain)) realm_domain = do_add_realm_domain(user_profile.realm, domain, allow_subdomains, acting_user=user_profile) return json_success( request, data={"new_domain": [realm_domain.id, realm_domain.domain]})
def handle(self, *args, **options): # type: (*Any, **str) -> None realm = get_realm(options["string_id"]) if options["op"] == "show": print("Domains for %s:" % (realm.string_id,)) for realm_domain in get_realm_domains(realm): if realm_domain["allow_subdomains"]: print(realm_domain["domain"] + " (subdomains allowed)") else: print(realm_domain["domain"] + " (subdomains not allowed)") sys.exit(0) domain = options['domain'].strip().lower() try: validate_domain(domain) except ValidationError as e: print(e.messages[0]) sys.exit(1) if options["op"] == "add": try: if not can_add_realm_domain(domain): print("The domain %(domain)s belongs to another organization." % {'domain': domain}) sys.exit(1) RealmDomain.objects.create(realm=realm, domain=domain, allow_subdomains=options["allow_subdomains"]) sys.exit(0) except IntegrityError: print("The domain %(domain)s is already a part of your organization." % {'domain': domain}) sys.exit(1) elif options["op"] == "remove": try: RealmDomain.objects.get(realm=realm, domain=domain).delete() sys.exit(0) except RealmDomain.DoesNotExist: print("No such entry found!") sys.exit(1) else: self.print_help("./manage.py", "realm_domain") sys.exit(1)
def update_realm( request: HttpRequest, user_profile: UserProfile, name: Optional[str] = REQ(validator=check_string, default=None), description: Optional[str] = REQ(validator=check_string, default=None), emails_restricted_to_domains: Optional[bool] = REQ(validator=check_bool, default=None), disallow_disposable_email_addresses: Optional[bool] = REQ( validator=check_bool, default=None), invite_required: Optional[bool] = REQ(validator=check_bool, default=None), invite_by_admins_only: Optional[bool] = REQ(validator=check_bool, default=None), name_changes_disabled: Optional[bool] = REQ(validator=check_bool, default=None), email_changes_disabled: Optional[bool] = REQ(validator=check_bool, default=None), avatar_changes_disabled: Optional[bool] = REQ(validator=check_bool, default=None), inline_image_preview: Optional[bool] = REQ(validator=check_bool, default=None), inline_url_embed_preview: Optional[bool] = REQ(validator=check_bool, default=None), add_emoji_by_admins_only: Optional[bool] = REQ(validator=check_bool, default=None), allow_message_deleting: Optional[bool] = REQ(validator=check_bool, default=None), message_content_delete_limit_seconds: Optional[int] = REQ( converter=to_non_negative_int, default=None), allow_message_editing: Optional[bool] = REQ(validator=check_bool, default=None), allow_community_topic_editing: Optional[bool] = REQ(validator=check_bool, default=None), mandatory_topics: Optional[bool] = REQ(validator=check_bool, default=None), message_content_edit_limit_seconds: Optional[int] = REQ( converter=to_non_negative_int, default=None), allow_edit_history: Optional[bool] = REQ(validator=check_bool, default=None), default_language: Optional[str] = REQ(validator=check_string, default=None), waiting_period_threshold: Optional[int] = REQ( converter=to_non_negative_int, default=None), authentication_methods: Optional[Dict[Any, Any]] = REQ(validator=check_dict([]), default=None), notifications_stream_id: Optional[int] = REQ(validator=check_int, default=None), signup_notifications_stream_id: Optional[int] = REQ(validator=check_int, default=None), message_retention_days: Optional[int] = REQ( converter=to_positive_or_allowed_int(Realm.RETAIN_MESSAGE_FOREVER), default=None), send_welcome_emails: Optional[bool] = REQ(validator=check_bool, default=None), digest_emails_enabled: Optional[bool] = REQ(validator=check_bool, default=None), message_content_allowed_in_email_notifications: Optional[bool] = REQ( validator=check_bool, default=None), bot_creation_policy: Optional[int] = REQ(validator=check_int_in( Realm.BOT_CREATION_POLICY_TYPES), default=None), create_stream_policy: Optional[int] = REQ(validator=check_int_in( Realm.COMMON_POLICY_TYPES), default=None), invite_to_stream_policy: Optional[int] = REQ(validator=check_int_in( Realm.COMMON_POLICY_TYPES), default=None), user_group_edit_policy: Optional[int] = REQ(validator=check_int_in( Realm.USER_GROUP_EDIT_POLICY_TYPES), default=None), private_message_policy: Optional[int] = REQ(validator=check_int_in( Realm.PRIVATE_MESSAGE_POLICY_TYPES), default=None), email_address_visibility: Optional[int] = REQ(validator=check_int_in( Realm.EMAIL_ADDRESS_VISIBILITY_TYPES), default=None), default_twenty_four_hour_time: Optional[bool] = REQ(validator=check_bool, default=None), video_chat_provider: Optional[int] = REQ(validator=check_int, default=None), google_hangouts_domain: Optional[str] = REQ(validator=check_string, default=None), default_code_block_language: Optional[str] = REQ(validator=check_string, default=None), digest_weekday: Optional[int] = REQ(validator=check_int_in( Realm.DIGEST_WEEKDAY_VALUES), default=None), ) -> HttpResponse: realm = user_profile.realm # Additional validation/error checking beyond types go here, so # the entire request can succeed or fail atomically. if default_language is not None and default_language not in get_available_language_codes( ): raise JsonableError(_("Invalid language '%s'") % (default_language, )) if description is not None and len(description) > 1000: return json_error(_("Organization description is too long.")) if name is not None and len(name) > Realm.MAX_REALM_NAME_LENGTH: return json_error(_("Organization name is too long.")) if authentication_methods is not None and True not in list( authentication_methods.values()): return json_error( _("At least one authentication method must be enabled.")) if (video_chat_provider is not None and video_chat_provider not in {p['id'] for p in Realm.VIDEO_CHAT_PROVIDERS.values()}): return json_error( _("Invalid video_chat_provider {}").format(video_chat_provider)) if video_chat_provider == Realm.VIDEO_CHAT_PROVIDERS['google_hangouts'][ 'id']: try: validate_domain(google_hangouts_domain) except ValidationError as e: return json_error(_('Invalid domain: {}').format(e.messages[0])) if message_retention_days is not None: realm.ensure_not_on_limited_plan() # The user of `locals()` here is a bit of a code smell, but it's # restricted to the elements present in realm.property_types. # # TODO: It should be possible to deduplicate this function up # further by some more advanced usage of the # `REQ/has_request_variables` extraction. req_vars = { k: v for k, v in list(locals().items()) if k in realm.property_types } data: Dict[str, Any] = {} for k, v in list(req_vars.items()): if v is not None and getattr(realm, k) != v: do_set_realm_property(realm, k, v) if isinstance(v, str): data[k] = 'updated' else: data[k] = v # The following realm properties do not fit the pattern above # authentication_methods is not supported by the do_set_realm_property # framework because of its bitfield. if authentication_methods is not None and ( realm.authentication_methods_dict() != authentication_methods): do_set_realm_authentication_methods(realm, authentication_methods) data['authentication_methods'] = authentication_methods # The message_editing settings are coupled to each other, and thus don't fit # into the do_set_realm_property framework. if ((allow_message_editing is not None and realm.allow_message_editing != allow_message_editing) or (message_content_edit_limit_seconds is not None and realm.message_content_edit_limit_seconds != message_content_edit_limit_seconds) or (allow_community_topic_editing is not None and realm.allow_community_topic_editing != allow_community_topic_editing)): if allow_message_editing is None: allow_message_editing = realm.allow_message_editing if message_content_edit_limit_seconds is None: message_content_edit_limit_seconds = realm.message_content_edit_limit_seconds if allow_community_topic_editing is None: allow_community_topic_editing = realm.allow_community_topic_editing do_set_realm_message_editing(realm, allow_message_editing, message_content_edit_limit_seconds, allow_community_topic_editing) data['allow_message_editing'] = allow_message_editing data[ 'message_content_edit_limit_seconds'] = message_content_edit_limit_seconds data['allow_community_topic_editing'] = allow_community_topic_editing if (message_content_delete_limit_seconds is not None and realm.message_content_delete_limit_seconds != message_content_delete_limit_seconds): do_set_realm_message_deleting(realm, message_content_delete_limit_seconds) data[ 'message_content_delete_limit_seconds'] = message_content_delete_limit_seconds # Realm.notifications_stream and Realm.signup_notifications_stream are not boolean, # str or integer field, and thus doesn't fit into the do_set_realm_property framework. if notifications_stream_id is not None: if realm.notifications_stream is None or (realm.notifications_stream.id != notifications_stream_id): new_notifications_stream = None if notifications_stream_id >= 0: (new_notifications_stream, recipient, sub) = access_stream_by_id(user_profile, notifications_stream_id) do_set_realm_notifications_stream(realm, new_notifications_stream, notifications_stream_id) data['notifications_stream_id'] = notifications_stream_id if signup_notifications_stream_id is not None: if realm.signup_notifications_stream is None or ( realm.signup_notifications_stream.id != signup_notifications_stream_id): new_signup_notifications_stream = None if signup_notifications_stream_id >= 0: (new_signup_notifications_stream, recipient, sub) = access_stream_by_id(user_profile, signup_notifications_stream_id) do_set_realm_signup_notifications_stream( realm, new_signup_notifications_stream, signup_notifications_stream_id) data[ 'signup_notifications_stream_id'] = signup_notifications_stream_id if default_code_block_language is not None: # Migrate '', used in the API to encode the default/None behavior of this feature. if default_code_block_language == '': data['default_code_block_language'] = None else: data['default_code_block_language'] = default_code_block_language return json_success(data)
def update_realm( request: HttpRequest, user_profile: UserProfile, name: Optional[str] = REQ(validator=check_string, default=None), description: Optional[str] = REQ(validator=check_string, default=None), emails_restricted_to_domains: Optional[bool] = REQ(validator=check_bool, default=None), disallow_disposable_email_addresses: Optional[bool] = REQ( validator=check_bool, default=None), invite_required: Optional[bool] = REQ(validator=check_bool, default=None), invite_by_admins_only: Optional[bool] = REQ(validator=check_bool, default=None), name_changes_disabled: Optional[bool] = REQ(validator=check_bool, default=None), email_changes_disabled: Optional[bool] = REQ(validator=check_bool, default=None), avatar_changes_disabled: Optional[bool] = REQ(validator=check_bool, default=None), inline_image_preview: Optional[bool] = REQ(validator=check_bool, default=None), inline_url_embed_preview: Optional[bool] = REQ(validator=check_bool, default=None), add_emoji_by_admins_only: Optional[bool] = REQ(validator=check_bool, default=None), allow_message_deleting: Optional[bool] = REQ(validator=check_bool, default=None), message_content_delete_limit_seconds: Optional[int] = REQ( converter=to_non_negative_int, default=None), allow_message_editing: Optional[bool] = REQ(validator=check_bool, default=None), allow_community_topic_editing: Optional[bool] = REQ(validator=check_bool, default=None), mandatory_topics: Optional[bool] = REQ(validator=check_bool, default=None), message_content_edit_limit_seconds: Optional[int] = REQ( converter=to_non_negative_int, default=None), allow_edit_history: Optional[bool] = REQ(validator=check_bool, default=None), default_language: Optional[str] = REQ(validator=check_string, default=None), waiting_period_threshold: Optional[int] = REQ( converter=to_non_negative_int, default=None), authentication_methods: Optional[Dict[Any, Any]] = REQ(validator=check_dict([]), default=None), notifications_stream_id: Optional[int] = REQ(validator=check_int, default=None), signup_notifications_stream_id: Optional[int] = REQ(validator=check_int, default=None), message_retention_days: Optional[int] = REQ( converter=to_not_negative_int_or_none, default=None), send_welcome_emails: Optional[bool] = REQ(validator=check_bool, default=None), digest_emails_enabled: Optional[bool] = REQ(validator=check_bool, default=None), message_content_allowed_in_email_notifications: Optional[bool] = REQ( validator=check_bool, default=None), bot_creation_policy: Optional[int] = REQ( converter=to_not_negative_int_or_none, default=None), create_stream_policy: Optional[int] = REQ(validator=check_int, default=None), invite_to_stream_policy: Optional[int] = REQ(validator=check_int, default=None), user_group_edit_policy: Optional[int] = REQ(validator=check_int, default=None), email_address_visibility: Optional[int] = REQ( converter=to_not_negative_int_or_none, default=None), default_twenty_four_hour_time: Optional[bool] = REQ(validator=check_bool, default=None), video_chat_provider: Optional[int] = REQ(validator=check_int, default=None), google_hangouts_domain: Optional[str] = REQ(validator=check_string, default=None), zoom_user_id: Optional[str] = REQ(validator=check_string, default=None), zoom_api_key: Optional[str] = REQ(validator=check_string, default=None), zoom_api_secret: Optional[str] = REQ(validator=check_string, default=None), digest_weekday: Optional[int] = REQ(validator=check_int, default=None), ) -> HttpResponse: realm = user_profile.realm # Additional validation/error checking beyond types go here, so # the entire request can succeed or fail atomically. if default_language is not None and default_language not in get_available_language_codes( ): raise JsonableError(_("Invalid language '%s'") % (default_language, )) if description is not None and len(description) > 1000: return json_error(_("Organization description is too long.")) if name is not None and len(name) > Realm.MAX_REALM_NAME_LENGTH: return json_error(_("Organization name is too long.")) if authentication_methods is not None and True not in list( authentication_methods.values()): return json_error( _("At least one authentication method must be enabled.")) if (video_chat_provider is not None and video_chat_provider not in set( p['id'] for p in Realm.VIDEO_CHAT_PROVIDERS.values())): return json_error( _("Invalid video chat provider {}").format(video_chat_provider)) if video_chat_provider == Realm.VIDEO_CHAT_PROVIDERS['google_hangouts'][ 'id']: try: validate_domain(google_hangouts_domain) except ValidationError as e: return json_error(_('Invalid domain: {}').format(e.messages[0])) if video_chat_provider == Realm.VIDEO_CHAT_PROVIDERS['zoom']['id']: if not zoom_api_secret: # Use the saved Zoom API secret if a new value isn't being sent zoom_api_secret = user_profile.realm.zoom_api_secret if not zoom_user_id: return json_error(_('User ID cannot be empty')) if not zoom_api_key: return json_error(_('API key cannot be empty')) if not zoom_api_secret: return json_error(_('API secret cannot be empty')) # If any of the Zoom settings have changed, validate the Zoom credentials. # # Technically, we could call some other API endpoint that # doesn't create a video call link, but this is a nicer # end-to-end test, since it verifies that the Zoom API user's # scopes includes the ability to create video calls, which is # the only capabiility we use. if ((zoom_user_id != realm.zoom_user_id or zoom_api_key != realm.zoom_api_key or zoom_api_secret != realm.zoom_api_secret) and not request_zoom_video_call_url(zoom_user_id, zoom_api_key, zoom_api_secret)): return json_error( _('Invalid credentials for the %(third_party_service)s API.') % dict(third_party_service="Zoom")) # Additional validation of enum-style values # TODO: Ideally, these checks would be automated rather than being manually maintained. if bot_creation_policy is not None and bot_creation_policy not in Realm.BOT_CREATION_POLICY_TYPES: return json_error( _("Invalid %(field_name)s") % dict(field_name="bot_creation_policy")) if email_address_visibility is not None and \ email_address_visibility not in Realm.EMAIL_ADDRESS_VISIBILITY_TYPES: return json_error( _("Invalid %(field_name)s") % dict(field_name="email_address_visibility")) if create_stream_policy is not None and \ create_stream_policy not in Realm.CREATE_STREAM_POLICY_TYPES: return json_error( _("Invalid %(field_name)s") % dict(field_name="create_stream_policy")) if invite_to_stream_policy is not None and \ invite_to_stream_policy not in Realm.INVITE_TO_STREAM_POLICY_TYPES: return json_error( _("Invalid %(field_name)s") % dict(field_name="invite_to_stream_policy")) if user_group_edit_policy is not None and \ user_group_edit_policy not in Realm.USER_GROUP_EDIT_POLICY_TYPES: return json_error( _("Invalid %(field_name)s") % dict(field_name="user_group_edit_policy")) # The user of `locals()` here is a bit of a code smell, but it's # restricted to the elements present in realm.property_types. # # TODO: It should be possible to deduplicate this function up # further by some more advanced usage of the # `REQ/has_request_variables` extraction. req_vars = { k: v for k, v in list(locals().items()) if k in realm.property_types } data = {} # type: Dict[str, Any] for k, v in list(req_vars.items()): if v is not None and getattr(realm, k) != v: do_set_realm_property(realm, k, v) if isinstance(v, str): data[k] = 'updated' else: data[k] = v # The following realm properties do not fit the pattern above # authentication_methods is not supported by the do_set_realm_property # framework because of its bitfield. if authentication_methods is not None and ( realm.authentication_methods_dict() != authentication_methods): do_set_realm_authentication_methods(realm, authentication_methods) data['authentication_methods'] = authentication_methods # The message_editing settings are coupled to each other, and thus don't fit # into the do_set_realm_property framework. if ((allow_message_editing is not None and realm.allow_message_editing != allow_message_editing) or (message_content_edit_limit_seconds is not None and realm.message_content_edit_limit_seconds != message_content_edit_limit_seconds) or (allow_community_topic_editing is not None and realm.allow_community_topic_editing != allow_community_topic_editing)): if allow_message_editing is None: allow_message_editing = realm.allow_message_editing if message_content_edit_limit_seconds is None: message_content_edit_limit_seconds = realm.message_content_edit_limit_seconds if allow_community_topic_editing is None: allow_community_topic_editing = realm.allow_community_topic_editing do_set_realm_message_editing(realm, allow_message_editing, message_content_edit_limit_seconds, allow_community_topic_editing) data['allow_message_editing'] = allow_message_editing data[ 'message_content_edit_limit_seconds'] = message_content_edit_limit_seconds data['allow_community_topic_editing'] = allow_community_topic_editing if (message_content_delete_limit_seconds is not None and realm.message_content_delete_limit_seconds != message_content_delete_limit_seconds): do_set_realm_message_deleting(realm, message_content_delete_limit_seconds) data[ 'message_content_delete_limit_seconds'] = message_content_delete_limit_seconds # Realm.notifications_stream and Realm.signup_notifications_stream are not boolean, # str or integer field, and thus doesn't fit into the do_set_realm_property framework. if notifications_stream_id is not None: if realm.notifications_stream is None or (realm.notifications_stream.id != notifications_stream_id): new_notifications_stream = None if notifications_stream_id >= 0: (new_notifications_stream, recipient, sub) = access_stream_by_id(user_profile, notifications_stream_id) do_set_realm_notifications_stream(realm, new_notifications_stream, notifications_stream_id) data['notifications_stream_id'] = notifications_stream_id if signup_notifications_stream_id is not None: if realm.signup_notifications_stream is None or ( realm.signup_notifications_stream.id != signup_notifications_stream_id): new_signup_notifications_stream = None if signup_notifications_stream_id >= 0: (new_signup_notifications_stream, recipient, sub) = access_stream_by_id(user_profile, signup_notifications_stream_id) do_set_realm_signup_notifications_stream( realm, new_signup_notifications_stream, signup_notifications_stream_id) data[ 'signup_notifications_stream_id'] = signup_notifications_stream_id return json_success(data)
def update_realm( request: HttpRequest, user_profile: UserProfile, name: Optional[str] = REQ(validator=check_string, default=None), description: Optional[str] = REQ(validator=check_string, default=None), restricted_to_domain: Optional[bool] = REQ(validator=check_bool, default=None), disallow_disposable_email_addresses: Optional[bool] = REQ( validator=check_bool, default=None), invite_required: Optional[bool] = REQ(validator=check_bool, default=None), invite_by_admins_only: Optional[bool] = REQ(validator=check_bool, default=None), name_changes_disabled: Optional[bool] = REQ(validator=check_bool, default=None), email_changes_disabled: Optional[bool] = REQ(validator=check_bool, default=None), inline_image_preview: Optional[bool] = REQ(validator=check_bool, default=None), inline_url_embed_preview: Optional[bool] = REQ(validator=check_bool, default=None), create_stream_by_admins_only: Optional[bool] = REQ(validator=check_bool, default=None), add_emoji_by_admins_only: Optional[bool] = REQ(validator=check_bool, default=None), allow_message_deleting: Optional[bool] = REQ(validator=check_bool, default=None), allow_message_editing: Optional[bool] = REQ(validator=check_bool, default=None), allow_community_topic_editing: Optional[bool] = REQ(validator=check_bool, default=None), mandatory_topics: Optional[bool] = REQ(validator=check_bool, default=None), message_content_edit_limit_seconds: Optional[int] = REQ( converter=to_non_negative_int, default=None), allow_edit_history: Optional[bool] = REQ(validator=check_bool, default=None), default_language: Optional[str] = REQ(validator=check_string, default=None), waiting_period_threshold: Optional[int] = REQ( converter=to_non_negative_int, default=None), authentication_methods: Optional[Dict[Any, Any]] = REQ(validator=check_dict([]), default=None), notifications_stream_id: Optional[int] = REQ(validator=check_int, default=None), signup_notifications_stream_id: Optional[int] = REQ(validator=check_int, default=None), message_retention_days: Optional[int] = REQ( converter=to_not_negative_int_or_none, default=None), send_welcome_emails: Optional[bool] = REQ(validator=check_bool, default=None), bot_creation_policy: Optional[int] = REQ( converter=to_not_negative_int_or_none, default=None), default_twenty_four_hour_time: Optional[bool] = REQ(validator=check_bool, default=None), video_chat_provider: Optional[str] = REQ(validator=check_string, default=None), google_hangouts_domain: Optional[str] = REQ(validator=check_string, default=None) ) -> HttpResponse: realm = user_profile.realm # Additional validation/error checking beyond types go here, so # the entire request can succeed or fail atomically. if default_language is not None and default_language not in get_available_language_codes( ): raise JsonableError(_("Invalid language '%s'" % (default_language, ))) if description is not None and len(description) > 1000: return json_error(_("Organization description is too long.")) if name is not None and len(name) > Realm.MAX_REALM_NAME_LENGTH: return json_error(_("Organization name is too long.")) if authentication_methods is not None and True not in list( authentication_methods.values()): return json_error( _("At least one authentication method must be enabled.")) if video_chat_provider == "Google Hangouts": try: validate_domain(google_hangouts_domain) except ValidationError as e: return json_error(_('Invalid domain: {}').format(e.messages[0])) # Additional validation of permissions values to add new bot if bot_creation_policy is not None and bot_creation_policy not in Realm.BOT_CREATION_POLICY_TYPES: return json_error(_("Invalid bot creation policy")) # The user of `locals()` here is a bit of a code smell, but it's # restricted to the elements present in realm.property_types. # # TODO: It should be possible to deduplicate this function up # further by some more advanced usage of the # `REQ/has_request_variables` extraction. req_vars = { k: v for k, v in list(locals().items()) if k in realm.property_types } data = {} # type: Dict[str, Any] for k, v in list(req_vars.items()): if v is not None and getattr(realm, k) != v: do_set_realm_property(realm, k, v) if isinstance(v, str): data[k] = 'updated' else: data[k] = v # The following realm properties do not fit the pattern above # authentication_methods is not supported by the do_set_realm_property # framework because of its bitfield. if authentication_methods is not None and ( realm.authentication_methods_dict() != authentication_methods): do_set_realm_authentication_methods(realm, authentication_methods) data['authentication_methods'] = authentication_methods # The message_editing settings are coupled to each other, and thus don't fit # into the do_set_realm_property framework. if ((allow_message_editing is not None and realm.allow_message_editing != allow_message_editing) or (message_content_edit_limit_seconds is not None and realm.message_content_edit_limit_seconds != message_content_edit_limit_seconds) or (allow_community_topic_editing is not None and realm.allow_community_topic_editing != allow_community_topic_editing)): if allow_message_editing is None: allow_message_editing = realm.allow_message_editing if message_content_edit_limit_seconds is None: message_content_edit_limit_seconds = realm.message_content_edit_limit_seconds if allow_community_topic_editing is None: allow_community_topic_editing = realm.allow_community_topic_editing do_set_realm_message_editing(realm, allow_message_editing, message_content_edit_limit_seconds, allow_community_topic_editing) data['allow_message_editing'] = allow_message_editing data[ 'message_content_edit_limit_seconds'] = message_content_edit_limit_seconds data['allow_community_topic_editing'] = allow_community_topic_editing # Realm.notifications_stream and Realm.signup_notifications_stream are not boolean, # str or integer field, and thus doesn't fit into the do_set_realm_property framework. if notifications_stream_id is not None: if realm.notifications_stream is None or (realm.notifications_stream.id != notifications_stream_id): new_notifications_stream = None if notifications_stream_id >= 0: (new_notifications_stream, recipient, sub) = access_stream_by_id(user_profile, notifications_stream_id) do_set_realm_notifications_stream(realm, new_notifications_stream, notifications_stream_id) data['notifications_stream_id'] = notifications_stream_id if signup_notifications_stream_id is not None: if realm.signup_notifications_stream is None or ( realm.signup_notifications_stream.id != signup_notifications_stream_id): new_signup_notifications_stream = None if signup_notifications_stream_id >= 0: (new_signup_notifications_stream, recipient, sub) = access_stream_by_id(user_profile, signup_notifications_stream_id) do_set_realm_signup_notifications_stream( realm, new_signup_notifications_stream, signup_notifications_stream_id) data[ 'signup_notifications_stream_id'] = signup_notifications_stream_id return json_success(data)
def update_realm( request: HttpRequest, user_profile: UserProfile, name: Optional[str]=REQ(validator=check_string, default=None), description: Optional[str]=REQ(validator=check_string, default=None), emails_restricted_to_domains: Optional[bool]=REQ(validator=check_bool, default=None), disallow_disposable_email_addresses: Optional[bool]=REQ(validator=check_bool, default=None), invite_required: Optional[bool]=REQ(validator=check_bool, default=None), invite_by_admins_only: Optional[bool]=REQ(validator=check_bool, default=None), name_changes_disabled: Optional[bool]=REQ(validator=check_bool, default=None), email_changes_disabled: Optional[bool]=REQ(validator=check_bool, default=None), inline_image_preview: Optional[bool]=REQ(validator=check_bool, default=None), inline_url_embed_preview: Optional[bool]=REQ(validator=check_bool, default=None), create_stream_by_admins_only: Optional[bool]=REQ(validator=check_bool, default=None), add_emoji_by_admins_only: Optional[bool]=REQ(validator=check_bool, default=None), allow_message_deleting: Optional[bool]=REQ(validator=check_bool, default=None), message_content_delete_limit_seconds: Optional[int]=REQ(converter=to_non_negative_int, default=None), allow_message_editing: Optional[bool]=REQ(validator=check_bool, default=None), allow_community_topic_editing: Optional[bool]=REQ(validator=check_bool, default=None), mandatory_topics: Optional[bool]=REQ(validator=check_bool, default=None), message_content_edit_limit_seconds: Optional[int]=REQ(converter=to_non_negative_int, default=None), allow_edit_history: Optional[bool]=REQ(validator=check_bool, default=None), default_language: Optional[str]=REQ(validator=check_string, default=None), waiting_period_threshold: Optional[int]=REQ(converter=to_non_negative_int, default=None), authentication_methods: Optional[Dict[Any, Any]]=REQ(validator=check_dict([]), default=None), notifications_stream_id: Optional[int]=REQ(validator=check_int, default=None), signup_notifications_stream_id: Optional[int]=REQ(validator=check_int, default=None), message_retention_days: Optional[int]=REQ(converter=to_not_negative_int_or_none, default=None), send_welcome_emails: Optional[bool]=REQ(validator=check_bool, default=None), message_content_allowed_in_email_notifications: Optional[bool]=REQ(validator=check_bool, default=None), bot_creation_policy: Optional[int]=REQ(converter=to_not_negative_int_or_none, default=None), email_address_visibility: Optional[int]=REQ(converter=to_not_negative_int_or_none, default=None), default_twenty_four_hour_time: Optional[bool]=REQ(validator=check_bool, default=None), video_chat_provider: Optional[str]=REQ(validator=check_string, default=None), google_hangouts_domain: Optional[str]=REQ(validator=check_string, default=None), zoom_user_id: Optional[str]=REQ(validator=check_string, default=None), zoom_api_key: Optional[str]=REQ(validator=check_string, default=None), zoom_api_secret: Optional[str]=REQ(validator=check_string, default=None), ) -> HttpResponse: realm = user_profile.realm # Additional validation/error checking beyond types go here, so # the entire request can succeed or fail atomically. if default_language is not None and default_language not in get_available_language_codes(): raise JsonableError(_("Invalid language '%s'" % (default_language,))) if description is not None and len(description) > 1000: return json_error(_("Organization description is too long.")) if name is not None and len(name) > Realm.MAX_REALM_NAME_LENGTH: return json_error(_("Organization name is too long.")) if authentication_methods is not None and True not in list(authentication_methods.values()): return json_error(_("At least one authentication method must be enabled.")) if video_chat_provider == "Google Hangouts": try: validate_domain(google_hangouts_domain) except ValidationError as e: return json_error(_('Invalid domain: {}').format(e.messages[0])) if video_chat_provider == "Zoom": if not zoom_user_id: return json_error(_('Invalid user ID: user ID cannot be empty')) if not zoom_api_key: return json_error(_('Invalid API key: API key cannot be empty')) if not zoom_api_secret: return json_error(_('Invalid API secret: API secret cannot be empty')) # Technically, we could call some other API endpoint that # doesn't create a video call link, but this is a nicer # end-to-end test, since it verifies that the Zoom API user's # scopes includes the ability to create video calls, which is # the only capabiility we use. if not request_zoom_video_call_url(zoom_user_id, zoom_api_key, zoom_api_secret): return json_error(_('Invalid credentials for the %(third_party_service)s API.') % dict( third_party_service="Zoom")) # Additional validation of enum-style values if bot_creation_policy is not None and bot_creation_policy not in Realm.BOT_CREATION_POLICY_TYPES: return json_error(_("Invalid bot creation policy")) if email_address_visibility is not None and \ email_address_visibility not in Realm.EMAIL_ADDRESS_VISIBILITY_TYPES: return json_error(_("Invalid email address visibility policy")) # The user of `locals()` here is a bit of a code smell, but it's # restricted to the elements present in realm.property_types. # # TODO: It should be possible to deduplicate this function up # further by some more advanced usage of the # `REQ/has_request_variables` extraction. req_vars = {k: v for k, v in list(locals().items()) if k in realm.property_types} data = {} # type: Dict[str, Any] for k, v in list(req_vars.items()): if v is not None and getattr(realm, k) != v: do_set_realm_property(realm, k, v) if isinstance(v, str): data[k] = 'updated' else: data[k] = v # The following realm properties do not fit the pattern above # authentication_methods is not supported by the do_set_realm_property # framework because of its bitfield. if authentication_methods is not None and (realm.authentication_methods_dict() != authentication_methods): do_set_realm_authentication_methods(realm, authentication_methods) data['authentication_methods'] = authentication_methods # The message_editing settings are coupled to each other, and thus don't fit # into the do_set_realm_property framework. if ((allow_message_editing is not None and realm.allow_message_editing != allow_message_editing) or (message_content_edit_limit_seconds is not None and realm.message_content_edit_limit_seconds != message_content_edit_limit_seconds) or (allow_community_topic_editing is not None and realm.allow_community_topic_editing != allow_community_topic_editing)): if allow_message_editing is None: allow_message_editing = realm.allow_message_editing if message_content_edit_limit_seconds is None: message_content_edit_limit_seconds = realm.message_content_edit_limit_seconds if allow_community_topic_editing is None: allow_community_topic_editing = realm.allow_community_topic_editing do_set_realm_message_editing(realm, allow_message_editing, message_content_edit_limit_seconds, allow_community_topic_editing) data['allow_message_editing'] = allow_message_editing data['message_content_edit_limit_seconds'] = message_content_edit_limit_seconds data['allow_community_topic_editing'] = allow_community_topic_editing if (message_content_delete_limit_seconds is not None and realm.message_content_delete_limit_seconds != message_content_delete_limit_seconds): do_set_realm_message_deleting(realm, message_content_delete_limit_seconds) data['message_content_delete_limit_seconds'] = message_content_delete_limit_seconds # Realm.notifications_stream and Realm.signup_notifications_stream are not boolean, # str or integer field, and thus doesn't fit into the do_set_realm_property framework. if notifications_stream_id is not None: if realm.notifications_stream is None or (realm.notifications_stream.id != notifications_stream_id): new_notifications_stream = None if notifications_stream_id >= 0: (new_notifications_stream, recipient, sub) = access_stream_by_id( user_profile, notifications_stream_id) do_set_realm_notifications_stream(realm, new_notifications_stream, notifications_stream_id) data['notifications_stream_id'] = notifications_stream_id if signup_notifications_stream_id is not None: if realm.signup_notifications_stream is None or (realm.signup_notifications_stream.id != signup_notifications_stream_id): new_signup_notifications_stream = None if signup_notifications_stream_id >= 0: (new_signup_notifications_stream, recipient, sub) = access_stream_by_id( user_profile, signup_notifications_stream_id) do_set_realm_signup_notifications_stream(realm, new_signup_notifications_stream, signup_notifications_stream_id) data['signup_notifications_stream_id'] = signup_notifications_stream_id return json_success(data)