Skip to content

Commit cb0bc5d

Browse files
committed
Add request.client_ip and USE_X_FORWARDED_FOR setting
1 parent 7cd1625 commit cb0bc5d

File tree

3 files changed

+21
-11
lines changed

3 files changed

+21
-11
lines changed

plain-redirection/plain/redirection/models.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,6 @@
1111
from plain.http import Request
1212

1313

14-
def _get_client_ip(request: Request) -> str | None:
15-
if x_forwarded_for := request.headers.get("X-Forwarded-For"):
16-
return x_forwarded_for.split(",")[0].strip()
17-
else:
18-
return request.meta.get("REMOTE_ADDR")
19-
20-
2114
@models.register_model
2215
class Redirect(models.Model):
2316
from_pattern: str = types.CharField(max_length=255)
@@ -126,7 +119,7 @@ def from_redirect(cls, redirect: Redirect, request: Request) -> RedirectLog:
126119
from_url=from_url,
127120
to_url=to_url,
128121
http_status=redirect.http_status,
129-
ip_address=_get_client_ip(request),
122+
ip_address=request.client_ip,
130123
user_agent=request.headers.get("User-Agent", ""),
131124
referrer=request.headers.get("Referer", ""),
132125
)
@@ -156,7 +149,7 @@ class NotFoundLog(models.Model):
156149
def from_request(cls, request: Request) -> NotFoundLog:
157150
return cls.query.create(
158151
url=request.build_absolute_uri(),
159-
ip_address=_get_client_ip(request),
152+
ip_address=request.client_ip,
160153
user_agent=request.headers.get("User-Agent", ""),
161154
referrer=request.headers.get("Referer", ""),
162155
)

plain/plain/http/request.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,21 @@ def port(self) -> str:
177177
port = self.meta["SERVER_PORT"]
178178
return str(port)
179179

180+
@cached_property
181+
def client_ip(self) -> str | None:
182+
"""Return the client's IP address.
183+
184+
If USE_X_FORWARDED_FOR is True, checks the X-Forwarded-For header first
185+
(using the first/leftmost IP). Otherwise returns REMOTE_ADDR directly.
186+
187+
Only enable USE_X_FORWARDED_FOR when behind a trusted proxy that
188+
overwrites the X-Forwarded-For header.
189+
"""
190+
if settings.USE_X_FORWARDED_FOR:
191+
if xff := self.headers.get("X-Forwarded-For"):
192+
return xff.split(",")[0].strip()
193+
return self.meta.get("REMOTE_ADDR")
194+
180195
def get_full_path(self, force_append_slash: bool = False) -> str:
181196
"""
182197
Return the full path for the request, including query string.

plain/plain/runtime/global_settings.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,12 @@
5555
# you may be opening yourself up to a security risk.
5656
HTTPS_PROXY_HEADER = None
5757

58-
# Whether to use the X-Forwarded-Host and X-Forwarded-Port headers
59-
# when determining the host and port for the request.
58+
# Whether to use the X-Forwarded-Host, X-Forwarded-Port, and X-Forwarded-For
59+
# headers when determining the host, port, and client IP for the request.
60+
# Only enable these when behind a trusted proxy that overwrites these headers.
6061
USE_X_FORWARDED_HOST = False
6162
USE_X_FORWARDED_PORT = False
63+
USE_X_FORWARDED_FOR = False
6264

6365
# A secret key for this particular Plain installation. Used in secret-key
6466
# hashing algorithms. Set this in your settings, or Plain will complain

0 commit comments

Comments
 (0)