mirror of
https://github.com/zulip/zulip.git
synced 2025-10-23 04:52:12 +00:00
zilencer: Rename registration takeover to registration transfer.
This is the final naming that we want, compared to the naming we merged in #32399. Includes renaming the API endpoints, but that should be fine as the original PR was just merged and this isn't deployed anywhere.
This commit is contained in:
committed by
Tim Abbott
parent
a23663e6f9
commit
7390eb2ed0
@@ -493,11 +493,11 @@ def maybe_enqueue_audit_log_upload(realm: Realm) -> None:
|
||||
|
||||
|
||||
SELF_HOSTING_REGISTRATION_TAKEOVER_CHALLENGE_TOKEN_REDIS_KEY = (
|
||||
"self_hosting_domain_takeover_challenge_verify"
|
||||
"self_hosting_domain_transfer_challenge_verify"
|
||||
)
|
||||
|
||||
|
||||
def prepare_for_registration_takeover_challenge(verification_secret: str) -> str:
|
||||
def prepare_for_registration_transfer_challenge(verification_secret: str) -> str:
|
||||
access_token = secrets.token_urlsafe(32)
|
||||
data_to_store = {"verification_secret": verification_secret, "access_token": access_token}
|
||||
redis_client.set(
|
||||
|
@@ -13,7 +13,7 @@ from typing_extensions import override
|
||||
from zerver.lib.management import ZulipBaseCommand, check_config
|
||||
from zerver.lib.remote_server import (
|
||||
PushBouncerSession,
|
||||
prepare_for_registration_takeover_challenge,
|
||||
prepare_for_registration_transfer_challenge,
|
||||
send_json_to_push_bouncer,
|
||||
send_server_data_to_push_bouncer,
|
||||
)
|
||||
@@ -41,7 +41,7 @@ class Command(ZulipBaseCommand):
|
||||
help="Automatically rotate your server's zulip_org_key",
|
||||
)
|
||||
action.add_argument(
|
||||
"--registration-takeover",
|
||||
"--registration-transfer",
|
||||
action="store_true",
|
||||
help="Overwrite pre-existing registration for the hostname",
|
||||
)
|
||||
@@ -114,8 +114,8 @@ class Command(ZulipBaseCommand):
|
||||
# enough about what happened.
|
||||
return
|
||||
|
||||
if options["registration_takeover"]:
|
||||
org_id, org_key = self.do_registration_takeover_flow(hostname)
|
||||
if options["registration_transfer"]:
|
||||
org_id, org_key = self.do_registration_transfer_flow(hostname)
|
||||
# We still want to proceed with a regular request to the registration endpoint,
|
||||
# as it'll update the registration with new information such as the contact email.
|
||||
request["zulip_org_id"] = org_id
|
||||
@@ -154,24 +154,24 @@ class Command(ZulipBaseCommand):
|
||||
)
|
||||
print("Mobile Push Notification Service registration successfully updated!")
|
||||
|
||||
if options["registration_takeover"]:
|
||||
if options["registration_transfer"]:
|
||||
print()
|
||||
print(
|
||||
"Make sure to restart the server next by running /home/zulip/deployments/current/scripts/restart-server "
|
||||
"so that the new credentials are reloaded."
|
||||
)
|
||||
|
||||
def do_registration_takeover_flow(self, hostname: str) -> tuple[str, str]:
|
||||
def do_registration_transfer_flow(self, hostname: str) -> tuple[str, str]:
|
||||
params = {"hostname": hostname}
|
||||
response = self._request_push_notification_bouncer_url(
|
||||
"/api/v1/remotes/server/register/takeover", params
|
||||
"/api/v1/remotes/server/register/transfer", params
|
||||
)
|
||||
verification_secret = response.json()["verification_secret"]
|
||||
|
||||
print(
|
||||
"Received a verification secret from the service. Preparing to serve it at the verification URL."
|
||||
)
|
||||
token_for_push_bouncer = prepare_for_registration_takeover_challenge(verification_secret)
|
||||
token_for_push_bouncer = prepare_for_registration_transfer_challenge(verification_secret)
|
||||
|
||||
print("Sending ACK to the service and awaiting completion of verification...")
|
||||
response = self._request_push_notification_bouncer_url(
|
||||
|
@@ -75,7 +75,7 @@ from zerver.lib.remote_server import (
|
||||
PushNotificationBouncerServerError,
|
||||
build_analytics_data,
|
||||
get_realms_info_for_push_bouncer,
|
||||
prepare_for_registration_takeover_challenge,
|
||||
prepare_for_registration_transfer_challenge,
|
||||
record_push_notifications_recently_working,
|
||||
redis_client,
|
||||
send_server_data_to_push_bouncer,
|
||||
@@ -111,7 +111,7 @@ from zerver.models.scheduled_jobs import NotificationTriggers
|
||||
from zerver.models.streams import get_stream
|
||||
from zilencer.auth import (
|
||||
REMOTE_SERVER_TAKEOVER_TOKEN_VALIDITY_SECONDS,
|
||||
generate_registration_takeover_verification_secret,
|
||||
generate_registration_transfer_verification_secret,
|
||||
)
|
||||
from zilencer.lib.remote_counts import MissingDataError
|
||||
from zilencer.models import RemoteZulipServerAuditLog
|
||||
@@ -5364,7 +5364,7 @@ class PushBouncerSignupTest(ZulipTestCase):
|
||||
"A server with hostname example.com already exists. "
|
||||
"If you control the hostname "
|
||||
"and want to transfer the registration to this server, you can run manage.py register_server "
|
||||
"with the --registration-takeover flag.",
|
||||
"with the --registration-transfer flag.",
|
||||
)
|
||||
|
||||
def test_register_contact_email_validation_rules(self) -> None:
|
||||
@@ -5433,13 +5433,13 @@ class RegistrationTakeoverFlowTest(ZulipTestCase):
|
||||
server = RemoteZulipServer.objects.get(uuid=self.zulip_org_id)
|
||||
|
||||
result = self.client_post(
|
||||
"/api/v1/remotes/server/register/takeover", {"hostname": self.hostname}
|
||||
"/api/v1/remotes/server/register/transfer", {"hostname": self.hostname}
|
||||
)
|
||||
self.assert_json_success(result)
|
||||
data = result.json()
|
||||
verification_secret = data["verification_secret"]
|
||||
|
||||
access_token = prepare_for_registration_takeover_challenge(verification_secret)
|
||||
access_token = prepare_for_registration_transfer_challenge(verification_secret)
|
||||
# First we query the host's endpoint for serving the verification_secret.
|
||||
result = self.client_post(f"/api/v1/zulip-services/verify/{access_token}/")
|
||||
self.assert_json_success(result)
|
||||
@@ -5469,7 +5469,7 @@ class RegistrationTakeoverFlowTest(ZulipTestCase):
|
||||
self.assertNotEqual(new_key, self.zulip_org_key)
|
||||
self.assertEqual(
|
||||
mock_log.output,
|
||||
["INFO:zilencer.views:verify_registration_takeover:host:example.com|success"],
|
||||
["INFO:zilencer.views:verify_registration_transfer:host:example.com|success"],
|
||||
)
|
||||
|
||||
# Verify the registration got updated accordingly.
|
||||
@@ -5485,7 +5485,7 @@ class RegistrationTakeoverFlowTest(ZulipTestCase):
|
||||
@override_settings(
|
||||
RATE_LIMITING=True,
|
||||
ABSOLUTE_USAGE_LIMITS_BY_ENDPOINT={
|
||||
"verify_registration_takeover_challenge_ack_endpoint": [(10, 2)]
|
||||
"verify_registration_transfer_challenge_ack_endpoint": [(10, 2)]
|
||||
},
|
||||
)
|
||||
@responses.activate
|
||||
@@ -5521,7 +5521,7 @@ class RegistrationTakeoverFlowTest(ZulipTestCase):
|
||||
self.assertEqual(
|
||||
mock_log.output,
|
||||
[
|
||||
"WARNING:zilencer.views:Rate limit exceeded for verify_registration_takeover_challenge_ack_endpoint"
|
||||
"WARNING:zilencer.views:Rate limit exceeded for verify_registration_transfer_challenge_ack_endpoint"
|
||||
],
|
||||
)
|
||||
|
||||
@@ -5548,7 +5548,7 @@ class RegistrationTakeoverFlowTest(ZulipTestCase):
|
||||
self.assert_json_error(result, "The verification secret is malformed")
|
||||
|
||||
with time_machine.travel(time_now, tick=False):
|
||||
verification_secret = generate_registration_takeover_verification_secret(self.hostname)
|
||||
verification_secret = generate_registration_transfer_verification_secret(self.hostname)
|
||||
responses.get(
|
||||
"https://example.com/api/v1/zulip-services/verify/sometoken/",
|
||||
json={"verification_secret": verification_secret},
|
||||
@@ -5568,7 +5568,7 @@ class RegistrationTakeoverFlowTest(ZulipTestCase):
|
||||
time_machine.travel(time_now, tick=False),
|
||||
mock.patch("zilencer.auth.REMOTE_SERVER_TAKEOVER_TOKEN_SALT", "foo"),
|
||||
):
|
||||
verification_secret = generate_registration_takeover_verification_secret(self.hostname)
|
||||
verification_secret = generate_registration_transfer_verification_secret(self.hostname)
|
||||
responses.get(
|
||||
"https://example.com/api/v1/zulip-services/verify/sometoken/",
|
||||
json={"verification_secret": verification_secret},
|
||||
@@ -5582,7 +5582,7 @@ class RegistrationTakeoverFlowTest(ZulipTestCase):
|
||||
|
||||
# Make sure a valid verification secret for one hostname does not work for another.
|
||||
with time_machine.travel(time_now, tick=False):
|
||||
verification_secret = generate_registration_takeover_verification_secret(
|
||||
verification_secret = generate_registration_transfer_verification_secret(
|
||||
"different.example.com"
|
||||
)
|
||||
responses.get(
|
||||
@@ -5616,7 +5616,7 @@ class RegistrationTakeoverFlowTest(ZulipTestCase):
|
||||
self.assertEqual(
|
||||
mock_log.output,
|
||||
[
|
||||
"INFO:zilencer.views:verify_registration_takeover:host:example.com|secret_not_prepared"
|
||||
"INFO:zilencer.views:verify_registration_transfer:host:example.com|secret_not_prepared"
|
||||
],
|
||||
)
|
||||
|
||||
@@ -5633,7 +5633,7 @@ class RegistrationTakeoverFlowTest(ZulipTestCase):
|
||||
)
|
||||
self.assert_json_error(result, "Error response received from the host: 403")
|
||||
self.assertIn(
|
||||
"verify_registration_takeover:host:example.com|exception:", mock_log.output[0]
|
||||
"verify_registration_transfer:host:example.com|exception:", mock_log.output[0]
|
||||
)
|
||||
|
||||
# SSLError:
|
||||
@@ -5649,7 +5649,7 @@ class RegistrationTakeoverFlowTest(ZulipTestCase):
|
||||
)
|
||||
self.assert_json_error(result, "SSL error occurred while communicating with the host.")
|
||||
self.assertIn(
|
||||
"verify_registration_takeover:host:example.com|exception:", mock_log.output[0]
|
||||
"verify_registration_transfer:host:example.com|exception:", mock_log.output[0]
|
||||
)
|
||||
|
||||
# ConnectionError:
|
||||
@@ -5667,7 +5667,7 @@ class RegistrationTakeoverFlowTest(ZulipTestCase):
|
||||
result, "Connection error occurred while communicating with the host."
|
||||
)
|
||||
self.assertIn(
|
||||
"verify_registration_takeover:host:example.com|exception:", mock_log.output[0]
|
||||
"verify_registration_transfer:host:example.com|exception:", mock_log.output[0]
|
||||
)
|
||||
|
||||
# Timeout:
|
||||
@@ -5683,7 +5683,7 @@ class RegistrationTakeoverFlowTest(ZulipTestCase):
|
||||
)
|
||||
self.assert_json_error(result, "The request timed out while communicating with the host.")
|
||||
self.assertIn(
|
||||
"verify_registration_takeover:host:example.com|exception:", mock_log.output[0]
|
||||
"verify_registration_transfer:host:example.com|exception:", mock_log.output[0]
|
||||
)
|
||||
|
||||
# Generic RequestException:
|
||||
@@ -5699,12 +5699,12 @@ class RegistrationTakeoverFlowTest(ZulipTestCase):
|
||||
)
|
||||
self.assert_json_error(result, "An error occurred while communicating with the host.")
|
||||
self.assertIn(
|
||||
"verify_registration_takeover:host:example.com|exception:", mock_log.output[0]
|
||||
"verify_registration_transfer:host:example.com|exception:", mock_log.output[0]
|
||||
)
|
||||
|
||||
def test_initiate_flow_for_unregistered_domain(self) -> None:
|
||||
result = self.client_post(
|
||||
"/api/v1/remotes/server/register/takeover",
|
||||
"/api/v1/remotes/server/register/transfer",
|
||||
{"hostname": "unregistered.example.com"},
|
||||
)
|
||||
self.assert_json_error(result, "unregistered.example.com not yet registered")
|
||||
@@ -5715,7 +5715,7 @@ class RegistrationTakeoverFlowTest(ZulipTestCase):
|
||||
)
|
||||
self.assert_json_error(result, "Verification secret not prepared")
|
||||
|
||||
valid_access_token = prepare_for_registration_takeover_challenge(verification_secret="foo")
|
||||
valid_access_token = prepare_for_registration_transfer_challenge(verification_secret="foo")
|
||||
result = self.client_get(
|
||||
f"/api/v1/zulip-services/verify/{valid_access_token}/",
|
||||
)
|
||||
|
@@ -247,7 +247,7 @@ class VerificationSecretNotPreparedError(JsonableError):
|
||||
|
||||
|
||||
@typed_endpoint_without_parameters
|
||||
def self_hosting_registration_takeover_challenge_verify(
|
||||
def self_hosting_registration_transfer_challenge_verify(
|
||||
request: HttpRequest, access_token: str
|
||||
) -> HttpResponse:
|
||||
json_data = redis_client.get(
|
||||
|
@@ -40,17 +40,17 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
ParamT = ParamSpec("ParamT")
|
||||
|
||||
REMOTE_SERVER_TAKEOVER_TOKEN_SALT = "remote_server_takeover"
|
||||
REMOTE_SERVER_TAKEOVER_TOKEN_SALT = "remote_server_transfer"
|
||||
REMOTE_SERVER_TAKEOVER_TOKEN_VALIDITY_SECONDS = 10
|
||||
|
||||
|
||||
def generate_registration_takeover_verification_secret(hostname: str) -> str:
|
||||
def generate_registration_transfer_verification_secret(hostname: str) -> str:
|
||||
signer = TimestampSigner(salt=REMOTE_SERVER_TAKEOVER_TOKEN_SALT)
|
||||
secret = base64.b16encode(signer.sign(hostname).encode()).decode()
|
||||
return secret
|
||||
|
||||
|
||||
def validate_registration_takeover_verification_secret(secret: str, hostname: str) -> None:
|
||||
def validate_registration_transfer_verification_secret(secret: str, hostname: str) -> None:
|
||||
signer = TimestampSigner(salt=REMOTE_SERVER_TAKEOVER_TOKEN_SALT)
|
||||
try:
|
||||
signed_data = base64.b16decode(secret).decode()
|
||||
|
@@ -13,10 +13,10 @@ from zilencer.views import (
|
||||
remote_server_notify_push,
|
||||
remote_server_post_analytics,
|
||||
remote_server_send_test_notification,
|
||||
take_over_remote_server_registration,
|
||||
transfer_remote_server_registration,
|
||||
unregister_all_remote_push_devices,
|
||||
unregister_remote_push_device,
|
||||
verify_registration_takeover_challenge_ack_endpoint,
|
||||
verify_registration_transfer_challenge_ack_endpoint,
|
||||
)
|
||||
|
||||
i18n_urlpatterns: Any = []
|
||||
@@ -30,10 +30,10 @@ push_bouncer_patterns = [
|
||||
remote_server_path("remotes/push/test_notification", POST=remote_server_send_test_notification),
|
||||
# Push signup doesn't use the REST API, since there's no auth.
|
||||
path("remotes/server/register", register_remote_server),
|
||||
path("remotes/server/register/takeover", take_over_remote_server_registration),
|
||||
path("remotes/server/register/transfer", transfer_remote_server_registration),
|
||||
path(
|
||||
"remotes/server/register/verify_challenge",
|
||||
verify_registration_takeover_challenge_ack_endpoint,
|
||||
verify_registration_transfer_challenge_ack_endpoint,
|
||||
),
|
||||
remote_server_path("remotes/server/deactivate", POST=deactivate_remote_server),
|
||||
# For receiving table data used in analytics and billing
|
||||
|
@@ -80,8 +80,8 @@ from zerver.models.realms import DisposableEmailError
|
||||
from zerver.views.push_notifications import validate_token
|
||||
from zilencer.auth import (
|
||||
InvalidZulipServerKeyError,
|
||||
generate_registration_takeover_verification_secret,
|
||||
validate_registration_takeover_verification_secret,
|
||||
generate_registration_transfer_verification_secret,
|
||||
validate_registration_transfer_verification_secret,
|
||||
)
|
||||
from zilencer.lib.remote_counts import MissingDataError
|
||||
from zilencer.models import (
|
||||
@@ -162,13 +162,13 @@ def validate_hostname_or_raise_error(hostname: str) -> None:
|
||||
@csrf_exempt
|
||||
@require_post
|
||||
@typed_endpoint
|
||||
def take_over_remote_server_registration(request: HttpRequest, *, hostname: str) -> HttpResponse:
|
||||
def transfer_remote_server_registration(request: HttpRequest, *, hostname: str) -> HttpResponse:
|
||||
validate_hostname_or_raise_error(hostname)
|
||||
|
||||
if not RemoteZulipServer.objects.filter(hostname=hostname).exists():
|
||||
raise JsonableError(_("{hostname} not yet registered").format(hostname=hostname))
|
||||
|
||||
verification_secret = generate_registration_takeover_verification_secret(hostname)
|
||||
verification_secret = generate_registration_transfer_verification_secret(hostname)
|
||||
return json_success(
|
||||
request,
|
||||
data={
|
||||
@@ -263,7 +263,7 @@ def register_remote_server(
|
||||
_(
|
||||
"A server with hostname {hostname} already exists. If you control the hostname "
|
||||
"and want to transfer the registration to this server, you can run manage.py register_server "
|
||||
"with the --registration-takeover flag."
|
||||
"with the --registration-transfer flag."
|
||||
).format(hostname=hostname)
|
||||
)
|
||||
|
||||
@@ -295,11 +295,11 @@ def register_remote_server(
|
||||
return json_success(request, data={"created": created})
|
||||
|
||||
|
||||
class RegistrationTakeOverVerificationSession(OutgoingSession):
|
||||
class RegistrationTransferVerificationSession(OutgoingSession):
|
||||
def __init__(self) -> None:
|
||||
# The generous timeout and retries here are likely to be unnecessary; a functional Zulip server should
|
||||
# respond instantly.
|
||||
super().__init__(role="verify_registration_takeover_challenge", timeout=5, max_retries=3)
|
||||
super().__init__(role="verify_registration_transfer_challenge", timeout=5, max_retries=3)
|
||||
|
||||
|
||||
class EndpointUsageRateLimitError(JsonableError):
|
||||
@@ -309,7 +309,7 @@ class EndpointUsageRateLimitError(JsonableError):
|
||||
|
||||
@csrf_exempt
|
||||
@typed_endpoint
|
||||
def verify_registration_takeover_challenge_ack_endpoint(
|
||||
def verify_registration_transfer_challenge_ack_endpoint(
|
||||
request: HttpRequest,
|
||||
*,
|
||||
hostname: str,
|
||||
@@ -334,13 +334,13 @@ def verify_registration_takeover_challenge_ack_endpoint(
|
||||
# attacker to fill up the bucket here, and issues can be handled adequately by
|
||||
# manual intervention.
|
||||
if settings.RATE_LIMITING:
|
||||
rate_limit_endpoint_absolute("verify_registration_takeover_challenge_ack_endpoint")
|
||||
rate_limit_endpoint_absolute("verify_registration_transfer_challenge_ack_endpoint")
|
||||
except RateLimitedError:
|
||||
# This rate limit being hit means we've either set the limits too low for legitimate use,
|
||||
# or the endpoint is being spammed. Ideally, we want this endpoint to always be operational
|
||||
# so this deserves logging a warning.
|
||||
logger.warning(
|
||||
"Rate limit exceeded for verify_registration_takeover_challenge_ack_endpoint"
|
||||
"Rate limit exceeded for verify_registration_transfer_challenge_ack_endpoint"
|
||||
)
|
||||
raise EndpointUsageRateLimitError(
|
||||
_(
|
||||
@@ -354,7 +354,7 @@ def verify_registration_takeover_challenge_ack_endpoint(
|
||||
except RemoteZulipServer.DoesNotExist:
|
||||
raise JsonableError(_("Registration not found for this hostname"))
|
||||
|
||||
session = RegistrationTakeOverVerificationSession()
|
||||
session = RegistrationTransferVerificationSession()
|
||||
url = urljoin(f"https://{hostname}", f"/api/v1/zulip-services/verify/{access_token}/")
|
||||
|
||||
exception_and_error_message: tuple[Exception, str] | None = None
|
||||
@@ -362,8 +362,8 @@ def verify_registration_takeover_challenge_ack_endpoint(
|
||||
response = session.get(url)
|
||||
response.raise_for_status()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
if check_takeover_challenge_response_secret_not_prepared(e.response):
|
||||
logger.info("verify_registration_takeover:host:%s|secret_not_prepared", hostname)
|
||||
if check_transfer_challenge_response_secret_not_prepared(e.response):
|
||||
logger.info("verify_registration_transfer:host:%s|secret_not_prepared", hostname)
|
||||
raise JsonableError(_("The host reported it has no verification secret."))
|
||||
|
||||
error_message = _("Error response received from the host: {status_code}").format(
|
||||
@@ -385,14 +385,14 @@ def verify_registration_takeover_challenge_ack_endpoint(
|
||||
|
||||
if exception_and_error_message is not None:
|
||||
exception, error_message = exception_and_error_message
|
||||
logger.info("verify_registration_takeover:host:%s|exception:%s", hostname, exception)
|
||||
logger.info("verify_registration_transfer:host:%s|exception:%s", hostname, exception)
|
||||
raise JsonableError(error_message)
|
||||
|
||||
data = response.json()
|
||||
verification_secret = data["verification_secret"]
|
||||
validate_registration_takeover_verification_secret(verification_secret, hostname)
|
||||
validate_registration_transfer_verification_secret(verification_secret, hostname)
|
||||
|
||||
logger.info("verify_registration_takeover:host:%s|success", hostname)
|
||||
logger.info("verify_registration_transfer:host:%s|success", hostname)
|
||||
new_secret_key = get_random_string(RemoteZulipServer.API_KEY_LENGTH)
|
||||
with transaction.atomic(durable=True):
|
||||
remote_server.api_key = new_secret_key
|
||||
@@ -410,7 +410,7 @@ def verify_registration_takeover_challenge_ack_endpoint(
|
||||
)
|
||||
|
||||
|
||||
def check_takeover_challenge_response_secret_not_prepared(response: requests.Response) -> bool:
|
||||
def check_transfer_challenge_response_secret_not_prepared(response: requests.Response) -> bool:
|
||||
secret_not_prepared = False
|
||||
try:
|
||||
secret_not_prepared = (
|
||||
|
@@ -343,7 +343,7 @@ RATE_LIMITING_RULES: dict[str, list[tuple[int, int]]] = {}
|
||||
# only, so we don't need a nice overriding system for them like we do
|
||||
# for RATE_LIMITING_RULES.
|
||||
ABSOLUTE_USAGE_LIMITS_BY_ENDPOINT = {
|
||||
"verify_registration_takeover_challenge_ack_endpoint": [
|
||||
"verify_registration_transfer_challenge_ack_endpoint": [
|
||||
# 30 requests per day
|
||||
(86400, 30),
|
||||
],
|
||||
|
@@ -102,7 +102,7 @@ from zerver.views.push_notifications import (
|
||||
self_hosting_auth_json_endpoint,
|
||||
self_hosting_auth_not_configured,
|
||||
self_hosting_auth_redirect_endpoint,
|
||||
self_hosting_registration_takeover_challenge_verify,
|
||||
self_hosting_registration_transfer_challenge_verify,
|
||||
send_test_push_notification_api,
|
||||
)
|
||||
from zerver.views.reactions import add_reaction, remove_reaction
|
||||
@@ -872,7 +872,7 @@ urls += [
|
||||
urls += [
|
||||
path(
|
||||
"api/v1/zulip-services/verify/<str:access_token>/",
|
||||
self_hosting_registration_takeover_challenge_verify,
|
||||
self_hosting_registration_transfer_challenge_verify,
|
||||
),
|
||||
]
|
||||
|
||||
|
Reference in New Issue
Block a user