Repository: getsentry/sentry

Issue:
Sentry ratelimit cannot be changed when using self-hosted
## Important Details

How are you running Sentry?

<!-- Please pick one of the following -->
On-Premise wo/ Docker, version 20.8.0

## Description
Sentry ratelimit cannot be changed when running on premises

## Steps to Reproduce

1. Go to web-interface, Admin/Settings
2. Set a non-zero ratelimit.
3. Get an error.

````
Oct  6 07:18:49 jsentry sentry[4128]: 10.100.33.5 - - [06/Oct/2020:04:18:49 +0000] "GET /api/0/internal/options/ HTTP/1.1" 200 20407 "https://sentry.findmykids.org/manage/settings/" "Mozilla/5.0 (X11; FreeBSD amd64; rv:76.0) Gecko/20100101 Firefox/76.0"
Oct  6 07:19:09 jsentry sentry[4128]: Traceback (most recent call last):
Oct  6 07:19:09 jsentry sentry[4128]:   File "/usr/local/lib/python2.7/site-packages/sentry-20.8.0-py2.7.egg/sentry/api/base.py", line 134, in handle_exception
Oct  6 07:19:09 jsentry sentry[4128]:     response = super(Endpoint, self).handle_exception(exc)
Oct  6 07:19:09 jsentry sentry[4128]:   File "/usr/local/lib/python2.7/site-packages/djangorestframework-3.6.4-py2.7.egg/rest_framework/views.py", line 449, in handle_exception
Oct  6 07:19:09 jsentry sentry[4128]:     self.raise_uncaught_exception(exc)
Oct  6 07:19:09 jsentry sentry[4128]:   File "/usr/local/lib/python2.7/site-packages/sentry-20.8.0-py2.7.egg/sentry/api/base.py", line 247, in dispatch
Oct  6 07:19:09 jsentry sentry[4128]:     response = handler(request, *args, **kwargs)
Oct  6 07:19:09 jsentry sentry[4128]:   File "/usr/local/lib/python2.7/site-packages/sentry-20.8.0-py2.7.egg/sentry/api/endpoints/system_options.py", line 74, in put
Oct  6 07:19:09 jsentry sentry[4128]:     options.set(k, v)
Oct  6 07:19:09 jsentry sentry[4128]:   File "/usr/local/lib/python2.7/site-packages/sentry-20.8.0-py2.7.egg/sentry/options/manager.py", line 83, in set
Oct  6 07:19:09 jsentry sentry[4128]:     "%r cannot be changed at runtime because it is configured on disk" % key
Oct  6 07:19:09 jsentry sentry[4128]: AssertionError: u'system.url-prefix' cannot be changed at runtime because it is configured on disk
Oct  6 07:19:09 jsentry sentry[4128]: 10.100.33.5 - - [06/Oct/2020:04:19:09 +0000] "PUT /api/0/internal/options/ HTTP/1.1" 500 746 "https://sentry.findmykids.org/manage/settings/" "Mozilla/5.0 (X11; FreeBSD amd64; rv:76.0) Gecko/20100101 Firefox/76.0"
````

### What you expected to happen
Ratelimit should be changeable from web-interface.

### Possible Solution
Stop sending system.url-prefix in PUT request ?



Discussion:
This seems to be the same issue in https://forum.sentry.io/t/config-yml-vs-manage-settings/10025.

Will look into this soon, thanks a lot for reporting!
I'm able to repro locally when `system.url-prefix` is set via `config.yml`.

Resolving patch:
diff --git a/src/sentry/api/endpoints/system_options.py b/src/sentry/api/endpoints/system_options.py
--- a/src/sentry/api/endpoints/system_options.py
+++ b/src/sentry/api/endpoints/system_options.py
@@ -72,10 +72,13 @@ def put(self, request):
                     options.delete(k)
                 else:
                     options.set(k, v)
-            except TypeError as e:
+            except (TypeError, AssertionError) as e:
+                # TODO(chadwhitacre): Use a custom exception for the
+                # immutability case, especially since asserts disappear with
+                # `python -O`.
                 return Response(
                     {
-                        "error": "invalid_type",
+                        "error": "invalid_type" if type(e) is TypeError else "immutable_option",
                         "errorDetail": {"option": k, "message": six.text_type(e)},
                     },
                     status=400,


Test patch:
diff --git a/tests/sentry/api/endpoints/test_system_options.py b/tests/sentry/api/endpoints/test_system_options.py
--- a/tests/sentry/api/endpoints/test_system_options.py
+++ b/tests/sentry/api/endpoints/test_system_options.py
@@ -67,6 +67,13 @@ def test_put_unknown_option(self):
         assert response.status_code == 400
         assert response.data["error"] == "unknown_option"
 
+    def test_put_hardwired_option(self):
+        with self.settings(SENTRY_OPTIONS={"system.url-prefix": "cheese"}):
+            self.login_as(user=self.user, superuser=True)
+            response = self.client.put(self.url, {"system.url-prefix": "bread"})
+            assert response.status_code == 400
+            assert response.data["error"] == "immutable_option"
+
     def test_put_simple(self):
         self.login_as(user=self.user, superuser=True)
         assert options.get("mail.host") != "lolcalhost"