-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #70 from koladev32/68-optional-ip-whitelisting-and…
…-blacklisting-using-database-storage 68 optional ip whitelisting and blacklisting using database storage
- Loading branch information
Showing
12 changed files
with
180 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
drf_simple_apikey/migrations/0003_apikey_blacklisted_ips_apikey_whitelisted_ips.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Generated by Django 5.0.3 on 2024-10-27 15:13 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
dependencies = [ | ||
("drf_simple_apikey", "0002_alter_apikey_options"), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name="apikey", | ||
name="blacklisted_ips", | ||
field=models.JSONField( | ||
blank=True, | ||
help_text="List of denied IP addresses for this API key.", | ||
null=True, | ||
), | ||
), | ||
migrations.AddField( | ||
model_name="apikey", | ||
name="whitelisted_ips", | ||
field=models.JSONField( | ||
blank=True, | ||
help_text="List of allowed IP addresses for this API key.", | ||
null=True, | ||
), | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import pytest | ||
from django.contrib.auth.models import User | ||
from rest_framework import exceptions | ||
from rest_framework.test import APIRequestFactory | ||
|
||
from drf_simple_apikey.backends import APIKeyAuthentication | ||
from drf_simple_apikey.settings import package_settings | ||
from .fixtures.api_key import active_api_key | ||
from .fixtures.user import user | ||
|
||
pytestmark = pytest.mark.django_db | ||
|
||
|
||
@pytest.fixture | ||
def valid_request_with_whitelisted_ip(user, active_api_key): | ||
"""Creates a valid request from a whitelisted IP address.""" | ||
factory = APIRequestFactory() | ||
api_key, key = active_api_key | ||
api_key.whitelisted_ips = ["127.0.0.1"] | ||
api_key.save() | ||
|
||
return factory.get( | ||
"/test-request/", | ||
REMOTE_ADDR="127.0.0.1", | ||
HTTP_AUTHORIZATION=f"{package_settings.AUTHENTICATION_KEYWORD_HEADER} {key}", | ||
) | ||
|
||
|
||
@pytest.fixture | ||
def valid_request_with_blacklisted_ip(user, active_api_key): | ||
"""Creates a request from a blacklisted IP address.""" | ||
factory = APIRequestFactory() | ||
api_key, key = active_api_key | ||
api_key.blacklisted_ips = ["127.0.0.1"] | ||
api_key.save() | ||
|
||
return factory.get( | ||
"/test-request/", | ||
REMOTE_ADDR="127.0.0.1", | ||
HTTP_AUTHORIZATION=f"{package_settings.AUTHENTICATION_KEYWORD_HEADER} {key}", | ||
) | ||
|
||
|
||
@pytest.fixture | ||
def request_with_unlisted_ip(user, active_api_key): | ||
"""Creates a request from an IP that is neither whitelisted nor blacklisted.""" | ||
factory = APIRequestFactory() | ||
api_key, key = active_api_key | ||
api_key.whitelisted_ips = ["192.168.0.1"] # Different IP than the request IP | ||
api_key.save() | ||
|
||
return factory.get( | ||
"/test-request/", | ||
REMOTE_ADDR="10.0.0.1", | ||
HTTP_AUTHORIZATION=f"{package_settings.AUTHENTICATION_KEYWORD_HEADER} {key}", | ||
) | ||
|
||
|
||
@pytest.fixture | ||
def api_key_authentication(): | ||
return APIKeyAuthentication() | ||
|
||
|
||
@pytest.mark.django_db | ||
class TestApiKeyAuthenticationWithIPManagement: | ||
pytestmark = pytest.mark.django_db | ||
|
||
def test_authenticate_valid_request_with_whitelisted_ip( | ||
self, valid_request_with_whitelisted_ip, api_key_authentication | ||
): | ||
"""Tests that a request from a whitelisted IP is authenticated successfully.""" | ||
entity, _ = api_key_authentication.authenticate( | ||
valid_request_with_whitelisted_ip | ||
) | ||
assert isinstance(entity, User) | ||
|
||
def test_authenticate_denied_for_blacklisted_ip( | ||
self, valid_request_with_blacklisted_ip, api_key_authentication | ||
): | ||
"""Tests that a request from a blacklisted IP is denied.""" | ||
with pytest.raises( | ||
exceptions.AuthenticationFailed, match=r"Access denied from blacklisted IP." | ||
): | ||
api_key_authentication.authenticate(valid_request_with_blacklisted_ip) | ||
|
||
def test_authenticate_denied_for_unlisted_ip_with_existing_whitelist( | ||
self, request_with_unlisted_ip, api_key_authentication | ||
): | ||
"""Tests that a request from an IP not in the whitelist is denied if a whitelist exists.""" | ||
with pytest.raises( | ||
exceptions.AuthenticationFailed, | ||
match=r"Access restricted to specific IP addresses.", | ||
): | ||
api_key_authentication.authenticate(request_with_unlisted_ip) | ||
|
||
def test_authenticate_allowed_for_request_with_no_ip_restrictions( | ||
self, user, active_api_key, api_key_authentication | ||
): | ||
"""Tests that a request with no IP restrictions is authenticated successfully.""" | ||
factory = APIRequestFactory() | ||
_, key = active_api_key | ||
|
||
request = factory.get( | ||
"/test-request/", | ||
REMOTE_ADDR="10.0.0.1", | ||
HTTP_AUTHORIZATION=f"{package_settings.AUTHENTICATION_KEYWORD_HEADER} {key}", | ||
) | ||
|
||
entity, _ = api_key_authentication.authenticate(request) | ||
assert isinstance(entity, User) |