authentik.policies.geoip.tests

geoip policy tests

  1"""geoip policy tests"""
  2
  3from django.test import TestCase
  4
  5from authentik.core.tests.utils import create_test_user
  6from authentik.events.models import Event, EventAction
  7from authentik.events.utils import get_user
  8from authentik.policies.engine import PolicyRequest, PolicyResult
  9from authentik.policies.exceptions import PolicyException
 10from authentik.policies.geoip.exceptions import GeoIPNotFoundException
 11from authentik.policies.geoip.models import GeoIPPolicy
 12
 13
 14class TestGeoIPPolicy(TestCase):
 15    """Test GeoIP Policy"""
 16
 17    def setUp(self):
 18        super().setUp()
 19        self.user = create_test_user()
 20        self.request = PolicyRequest(self.user)
 21
 22        self.context_disabled_geoip = {}
 23        self.context_unknown_ip = {"asn": None, "geoip": None}
 24        # 8.8.8.8
 25        self.context = {
 26            "asn": {"asn": 15169, "as_org": "GOOGLE", "network": "8.8.8.0/24"},
 27            "geoip": {
 28                "continent": "NA",
 29                "country": "US",
 30                "lat": 37.751,
 31                "long": -97.822,
 32                "city": "",
 33            },
 34        }
 35
 36        self.matching_asns = [13335, 15169]
 37        self.matching_countries = ["US", "CA"]
 38        self.mismatching_asns = [1, 2]
 39        self.mismatching_countries = ["MX", "UA"]
 40
 41    def enrich_context_disabled_geoip(self):
 42        pass
 43
 44    def enrich_context_unknown_ip(self):
 45        self.request.context["asn"] = self.context_unknown_ip["asn"]
 46        self.request.context["geoip"] = self.context_unknown_ip["geoip"]
 47
 48    def enrich_context(self):
 49        self.request.context["asn"] = self.context["asn"]
 50        self.request.context["geoip"] = self.context["geoip"]
 51
 52    def test_disabled_geoip(self):
 53        """Test that disabled GeoIP raises PolicyException with GeoIPNotFoundException"""
 54        self.enrich_context_disabled_geoip()
 55        policy = GeoIPPolicy.objects.create(
 56            asns=self.matching_asns, countries=self.matching_countries
 57        )
 58
 59        with self.assertRaises(PolicyException) as cm:
 60            policy.passes(self.request)
 61
 62        self.assertIsInstance(cm.exception.src_exc, GeoIPNotFoundException)
 63
 64    def test_unknown_ip(self):
 65        """Test that unknown IP raises PolicyException with GeoIPNotFoundException"""
 66        self.enrich_context_unknown_ip()
 67        policy = GeoIPPolicy.objects.create(
 68            asns=self.matching_asns, countries=self.matching_countries
 69        )
 70
 71        with self.assertRaises(PolicyException) as cm:
 72            policy.passes(self.request)
 73
 74        self.assertIsInstance(cm.exception.src_exc, GeoIPNotFoundException)
 75
 76    def test_empty_policy(self):
 77        """Test that empty policy passes"""
 78        self.enrich_context()
 79        policy = GeoIPPolicy.objects.create()
 80
 81        result: PolicyResult = policy.passes(self.request)
 82
 83        self.assertTrue(result.passing)
 84
 85    def test_policy_with_matching_asns(self):
 86        """Test that a policy with matching ASNs passes"""
 87        self.enrich_context()
 88        policy = GeoIPPolicy.objects.create(asns=self.matching_asns)
 89
 90        result: PolicyResult = policy.passes(self.request)
 91
 92        self.assertTrue(result.passing)
 93
 94    def test_policy_with_mismatching_asns(self):
 95        """Test that a policy with mismatching ASNs fails"""
 96        self.enrich_context()
 97        policy = GeoIPPolicy.objects.create(asns=self.mismatching_asns)
 98
 99        result: PolicyResult = policy.passes(self.request)
100
101        self.assertFalse(result.passing)
102
103    def test_policy_with_matching_countries(self):
104        """Test that a policy with matching countries passes"""
105        self.enrich_context()
106        policy = GeoIPPolicy.objects.create(countries=self.matching_countries)
107
108        result: PolicyResult = policy.passes(self.request)
109
110        self.assertTrue(result.passing)
111
112    def test_policy_with_mismatching_countries(self):
113        """Test that a policy with mismatching countries fails"""
114        self.enrich_context()
115        policy = GeoIPPolicy.objects.create(countries=self.mismatching_countries)
116
117        result: PolicyResult = policy.passes(self.request)
118
119        self.assertFalse(result.passing)
120
121    def test_policy_requires_only_one_match(self):
122        """Test that a policy with one matching value passes"""
123        self.enrich_context()
124        policy = GeoIPPolicy.objects.create(
125            asns=self.mismatching_asns, countries=self.matching_countries
126        )
127
128        result: PolicyResult = policy.passes(self.request)
129
130        self.assertTrue(result.passing)
131
132    def test_history(self):
133        """Test history checks"""
134        Event.objects.create(
135            action=EventAction.LOGIN,
136            user=get_user(self.user),
137            context={
138                # Random location in Canada
139                "geo": {"lat": 55.868351, "long": -104.441011},
140            },
141        )
142        # Random location in Poland
143        self.request.context["geoip"] = {"lat": 50.950613, "long": 20.363679}
144
145        policy = GeoIPPolicy.objects.create(check_history_distance=True)
146
147        result: PolicyResult = policy.passes(self.request)
148        self.assertFalse(result.passing)
149
150    def test_history_no_data(self):
151        """Test history checks (with no geoip data in context)"""
152        Event.objects.create(
153            action=EventAction.LOGIN,
154            user=get_user(self.user),
155            context={
156                # Random location in Canada
157                "geo": {"lat": 55.868351, "long": -104.441011},
158            },
159        )
160
161        policy = GeoIPPolicy.objects.create(check_history_distance=True)
162
163        result: PolicyResult = policy.passes(self.request)
164        self.assertFalse(result.passing)
165
166    def test_history_impossible_travel_failing(self):
167        """Test history checks"""
168        Event.objects.create(
169            action=EventAction.LOGIN,
170            user=get_user(self.user),
171            context={
172                # Random location in Canada
173                "geo": {"lat": 55.868351, "long": -104.441011},
174            },
175        )
176        # Random location in Poland
177        self.request.context["geoip"] = {"lat": 50.950613, "long": 20.363679}
178
179        policy = GeoIPPolicy.objects.create(check_impossible_travel=True)
180
181        result: PolicyResult = policy.passes(self.request)
182        self.assertFalse(result.passing)
183
184    def test_history_impossible_travel_passing(self):
185        """Test history checks"""
186        Event.objects.create(
187            action=EventAction.LOGIN,
188            user=get_user(self.user),
189            context={
190                # Random location in Canada
191                "geo": {"lat": 55.868351, "long": -104.441011},
192            },
193        )
194        # Same location
195        self.request.context["geoip"] = {"lat": 55.868351, "long": -104.441011}
196
197        policy = GeoIPPolicy.objects.create(check_impossible_travel=True)
198
199        result: PolicyResult = policy.passes(self.request)
200        self.assertTrue(result.passing)
201
202    def test_history_no_geoip(self):
203        """Test history checks (previous login with no geoip data)"""
204        Event.objects.create(
205            action=EventAction.LOGIN,
206            user=get_user(self.user),
207            context={},
208        )
209        # Random location in Poland
210        self.request.context["geoip"] = {"lat": 50.950613, "long": 20.363679}
211
212        policy = GeoIPPolicy.objects.create(check_history_distance=True)
213
214        result: PolicyResult = policy.passes(self.request)
215        self.assertFalse(result.passing)
216
217    def test_impossible_travel_no_geoip(self):
218        """Test impossible travel checks (previous login with no geoip data)"""
219        Event.objects.create(
220            action=EventAction.LOGIN,
221            user=get_user(self.user),
222            context={},
223        )
224        # Random location in Poland
225        self.request.context["geoip"] = {"lat": 50.950613, "long": 20.363679}
226
227        policy = GeoIPPolicy.objects.create(check_impossible_travel=True)
228
229        result: PolicyResult = policy.passes(self.request)
230        self.assertFalse(result.passing)
class TestGeoIPPolicy(django.test.testcases.TestCase):
 15class TestGeoIPPolicy(TestCase):
 16    """Test GeoIP Policy"""
 17
 18    def setUp(self):
 19        super().setUp()
 20        self.user = create_test_user()
 21        self.request = PolicyRequest(self.user)
 22
 23        self.context_disabled_geoip = {}
 24        self.context_unknown_ip = {"asn": None, "geoip": None}
 25        # 8.8.8.8
 26        self.context = {
 27            "asn": {"asn": 15169, "as_org": "GOOGLE", "network": "8.8.8.0/24"},
 28            "geoip": {
 29                "continent": "NA",
 30                "country": "US",
 31                "lat": 37.751,
 32                "long": -97.822,
 33                "city": "",
 34            },
 35        }
 36
 37        self.matching_asns = [13335, 15169]
 38        self.matching_countries = ["US", "CA"]
 39        self.mismatching_asns = [1, 2]
 40        self.mismatching_countries = ["MX", "UA"]
 41
 42    def enrich_context_disabled_geoip(self):
 43        pass
 44
 45    def enrich_context_unknown_ip(self):
 46        self.request.context["asn"] = self.context_unknown_ip["asn"]
 47        self.request.context["geoip"] = self.context_unknown_ip["geoip"]
 48
 49    def enrich_context(self):
 50        self.request.context["asn"] = self.context["asn"]
 51        self.request.context["geoip"] = self.context["geoip"]
 52
 53    def test_disabled_geoip(self):
 54        """Test that disabled GeoIP raises PolicyException with GeoIPNotFoundException"""
 55        self.enrich_context_disabled_geoip()
 56        policy = GeoIPPolicy.objects.create(
 57            asns=self.matching_asns, countries=self.matching_countries
 58        )
 59
 60        with self.assertRaises(PolicyException) as cm:
 61            policy.passes(self.request)
 62
 63        self.assertIsInstance(cm.exception.src_exc, GeoIPNotFoundException)
 64
 65    def test_unknown_ip(self):
 66        """Test that unknown IP raises PolicyException with GeoIPNotFoundException"""
 67        self.enrich_context_unknown_ip()
 68        policy = GeoIPPolicy.objects.create(
 69            asns=self.matching_asns, countries=self.matching_countries
 70        )
 71
 72        with self.assertRaises(PolicyException) as cm:
 73            policy.passes(self.request)
 74
 75        self.assertIsInstance(cm.exception.src_exc, GeoIPNotFoundException)
 76
 77    def test_empty_policy(self):
 78        """Test that empty policy passes"""
 79        self.enrich_context()
 80        policy = GeoIPPolicy.objects.create()
 81
 82        result: PolicyResult = policy.passes(self.request)
 83
 84        self.assertTrue(result.passing)
 85
 86    def test_policy_with_matching_asns(self):
 87        """Test that a policy with matching ASNs passes"""
 88        self.enrich_context()
 89        policy = GeoIPPolicy.objects.create(asns=self.matching_asns)
 90
 91        result: PolicyResult = policy.passes(self.request)
 92
 93        self.assertTrue(result.passing)
 94
 95    def test_policy_with_mismatching_asns(self):
 96        """Test that a policy with mismatching ASNs fails"""
 97        self.enrich_context()
 98        policy = GeoIPPolicy.objects.create(asns=self.mismatching_asns)
 99
100        result: PolicyResult = policy.passes(self.request)
101
102        self.assertFalse(result.passing)
103
104    def test_policy_with_matching_countries(self):
105        """Test that a policy with matching countries passes"""
106        self.enrich_context()
107        policy = GeoIPPolicy.objects.create(countries=self.matching_countries)
108
109        result: PolicyResult = policy.passes(self.request)
110
111        self.assertTrue(result.passing)
112
113    def test_policy_with_mismatching_countries(self):
114        """Test that a policy with mismatching countries fails"""
115        self.enrich_context()
116        policy = GeoIPPolicy.objects.create(countries=self.mismatching_countries)
117
118        result: PolicyResult = policy.passes(self.request)
119
120        self.assertFalse(result.passing)
121
122    def test_policy_requires_only_one_match(self):
123        """Test that a policy with one matching value passes"""
124        self.enrich_context()
125        policy = GeoIPPolicy.objects.create(
126            asns=self.mismatching_asns, countries=self.matching_countries
127        )
128
129        result: PolicyResult = policy.passes(self.request)
130
131        self.assertTrue(result.passing)
132
133    def test_history(self):
134        """Test history checks"""
135        Event.objects.create(
136            action=EventAction.LOGIN,
137            user=get_user(self.user),
138            context={
139                # Random location in Canada
140                "geo": {"lat": 55.868351, "long": -104.441011},
141            },
142        )
143        # Random location in Poland
144        self.request.context["geoip"] = {"lat": 50.950613, "long": 20.363679}
145
146        policy = GeoIPPolicy.objects.create(check_history_distance=True)
147
148        result: PolicyResult = policy.passes(self.request)
149        self.assertFalse(result.passing)
150
151    def test_history_no_data(self):
152        """Test history checks (with no geoip data in context)"""
153        Event.objects.create(
154            action=EventAction.LOGIN,
155            user=get_user(self.user),
156            context={
157                # Random location in Canada
158                "geo": {"lat": 55.868351, "long": -104.441011},
159            },
160        )
161
162        policy = GeoIPPolicy.objects.create(check_history_distance=True)
163
164        result: PolicyResult = policy.passes(self.request)
165        self.assertFalse(result.passing)
166
167    def test_history_impossible_travel_failing(self):
168        """Test history checks"""
169        Event.objects.create(
170            action=EventAction.LOGIN,
171            user=get_user(self.user),
172            context={
173                # Random location in Canada
174                "geo": {"lat": 55.868351, "long": -104.441011},
175            },
176        )
177        # Random location in Poland
178        self.request.context["geoip"] = {"lat": 50.950613, "long": 20.363679}
179
180        policy = GeoIPPolicy.objects.create(check_impossible_travel=True)
181
182        result: PolicyResult = policy.passes(self.request)
183        self.assertFalse(result.passing)
184
185    def test_history_impossible_travel_passing(self):
186        """Test history checks"""
187        Event.objects.create(
188            action=EventAction.LOGIN,
189            user=get_user(self.user),
190            context={
191                # Random location in Canada
192                "geo": {"lat": 55.868351, "long": -104.441011},
193            },
194        )
195        # Same location
196        self.request.context["geoip"] = {"lat": 55.868351, "long": -104.441011}
197
198        policy = GeoIPPolicy.objects.create(check_impossible_travel=True)
199
200        result: PolicyResult = policy.passes(self.request)
201        self.assertTrue(result.passing)
202
203    def test_history_no_geoip(self):
204        """Test history checks (previous login with no geoip data)"""
205        Event.objects.create(
206            action=EventAction.LOGIN,
207            user=get_user(self.user),
208            context={},
209        )
210        # Random location in Poland
211        self.request.context["geoip"] = {"lat": 50.950613, "long": 20.363679}
212
213        policy = GeoIPPolicy.objects.create(check_history_distance=True)
214
215        result: PolicyResult = policy.passes(self.request)
216        self.assertFalse(result.passing)
217
218    def test_impossible_travel_no_geoip(self):
219        """Test impossible travel checks (previous login with no geoip data)"""
220        Event.objects.create(
221            action=EventAction.LOGIN,
222            user=get_user(self.user),
223            context={},
224        )
225        # Random location in Poland
226        self.request.context["geoip"] = {"lat": 50.950613, "long": 20.363679}
227
228        policy = GeoIPPolicy.objects.create(check_impossible_travel=True)
229
230        result: PolicyResult = policy.passes(self.request)
231        self.assertFalse(result.passing)

Test GeoIP Policy

def setUp(self):
18    def setUp(self):
19        super().setUp()
20        self.user = create_test_user()
21        self.request = PolicyRequest(self.user)
22
23        self.context_disabled_geoip = {}
24        self.context_unknown_ip = {"asn": None, "geoip": None}
25        # 8.8.8.8
26        self.context = {
27            "asn": {"asn": 15169, "as_org": "GOOGLE", "network": "8.8.8.0/24"},
28            "geoip": {
29                "continent": "NA",
30                "country": "US",
31                "lat": 37.751,
32                "long": -97.822,
33                "city": "",
34            },
35        }
36
37        self.matching_asns = [13335, 15169]
38        self.matching_countries = ["US", "CA"]
39        self.mismatching_asns = [1, 2]
40        self.mismatching_countries = ["MX", "UA"]

Hook method for setting up the test fixture before exercising it.

def enrich_context_disabled_geoip(self):
42    def enrich_context_disabled_geoip(self):
43        pass
def enrich_context_unknown_ip(self):
45    def enrich_context_unknown_ip(self):
46        self.request.context["asn"] = self.context_unknown_ip["asn"]
47        self.request.context["geoip"] = self.context_unknown_ip["geoip"]
def enrich_context(self):
49    def enrich_context(self):
50        self.request.context["asn"] = self.context["asn"]
51        self.request.context["geoip"] = self.context["geoip"]
def test_disabled_geoip(self):
53    def test_disabled_geoip(self):
54        """Test that disabled GeoIP raises PolicyException with GeoIPNotFoundException"""
55        self.enrich_context_disabled_geoip()
56        policy = GeoIPPolicy.objects.create(
57            asns=self.matching_asns, countries=self.matching_countries
58        )
59
60        with self.assertRaises(PolicyException) as cm:
61            policy.passes(self.request)
62
63        self.assertIsInstance(cm.exception.src_exc, GeoIPNotFoundException)

Test that disabled GeoIP raises PolicyException with GeoIPNotFoundException

def test_unknown_ip(self):
65    def test_unknown_ip(self):
66        """Test that unknown IP raises PolicyException with GeoIPNotFoundException"""
67        self.enrich_context_unknown_ip()
68        policy = GeoIPPolicy.objects.create(
69            asns=self.matching_asns, countries=self.matching_countries
70        )
71
72        with self.assertRaises(PolicyException) as cm:
73            policy.passes(self.request)
74
75        self.assertIsInstance(cm.exception.src_exc, GeoIPNotFoundException)

Test that unknown IP raises PolicyException with GeoIPNotFoundException

def test_empty_policy(self):
77    def test_empty_policy(self):
78        """Test that empty policy passes"""
79        self.enrich_context()
80        policy = GeoIPPolicy.objects.create()
81
82        result: PolicyResult = policy.passes(self.request)
83
84        self.assertTrue(result.passing)

Test that empty policy passes

def test_policy_with_matching_asns(self):
86    def test_policy_with_matching_asns(self):
87        """Test that a policy with matching ASNs passes"""
88        self.enrich_context()
89        policy = GeoIPPolicy.objects.create(asns=self.matching_asns)
90
91        result: PolicyResult = policy.passes(self.request)
92
93        self.assertTrue(result.passing)

Test that a policy with matching ASNs passes

def test_policy_with_mismatching_asns(self):
 95    def test_policy_with_mismatching_asns(self):
 96        """Test that a policy with mismatching ASNs fails"""
 97        self.enrich_context()
 98        policy = GeoIPPolicy.objects.create(asns=self.mismatching_asns)
 99
100        result: PolicyResult = policy.passes(self.request)
101
102        self.assertFalse(result.passing)

Test that a policy with mismatching ASNs fails

def test_policy_with_matching_countries(self):
104    def test_policy_with_matching_countries(self):
105        """Test that a policy with matching countries passes"""
106        self.enrich_context()
107        policy = GeoIPPolicy.objects.create(countries=self.matching_countries)
108
109        result: PolicyResult = policy.passes(self.request)
110
111        self.assertTrue(result.passing)

Test that a policy with matching countries passes

def test_policy_with_mismatching_countries(self):
113    def test_policy_with_mismatching_countries(self):
114        """Test that a policy with mismatching countries fails"""
115        self.enrich_context()
116        policy = GeoIPPolicy.objects.create(countries=self.mismatching_countries)
117
118        result: PolicyResult = policy.passes(self.request)
119
120        self.assertFalse(result.passing)

Test that a policy with mismatching countries fails

def test_policy_requires_only_one_match(self):
122    def test_policy_requires_only_one_match(self):
123        """Test that a policy with one matching value passes"""
124        self.enrich_context()
125        policy = GeoIPPolicy.objects.create(
126            asns=self.mismatching_asns, countries=self.matching_countries
127        )
128
129        result: PolicyResult = policy.passes(self.request)
130
131        self.assertTrue(result.passing)

Test that a policy with one matching value passes

def test_history(self):
133    def test_history(self):
134        """Test history checks"""
135        Event.objects.create(
136            action=EventAction.LOGIN,
137            user=get_user(self.user),
138            context={
139                # Random location in Canada
140                "geo": {"lat": 55.868351, "long": -104.441011},
141            },
142        )
143        # Random location in Poland
144        self.request.context["geoip"] = {"lat": 50.950613, "long": 20.363679}
145
146        policy = GeoIPPolicy.objects.create(check_history_distance=True)
147
148        result: PolicyResult = policy.passes(self.request)
149        self.assertFalse(result.passing)

Test history checks

def test_history_no_data(self):
151    def test_history_no_data(self):
152        """Test history checks (with no geoip data in context)"""
153        Event.objects.create(
154            action=EventAction.LOGIN,
155            user=get_user(self.user),
156            context={
157                # Random location in Canada
158                "geo": {"lat": 55.868351, "long": -104.441011},
159            },
160        )
161
162        policy = GeoIPPolicy.objects.create(check_history_distance=True)
163
164        result: PolicyResult = policy.passes(self.request)
165        self.assertFalse(result.passing)

Test history checks (with no geoip data in context)

def test_history_impossible_travel_failing(self):
167    def test_history_impossible_travel_failing(self):
168        """Test history checks"""
169        Event.objects.create(
170            action=EventAction.LOGIN,
171            user=get_user(self.user),
172            context={
173                # Random location in Canada
174                "geo": {"lat": 55.868351, "long": -104.441011},
175            },
176        )
177        # Random location in Poland
178        self.request.context["geoip"] = {"lat": 50.950613, "long": 20.363679}
179
180        policy = GeoIPPolicy.objects.create(check_impossible_travel=True)
181
182        result: PolicyResult = policy.passes(self.request)
183        self.assertFalse(result.passing)

Test history checks

def test_history_impossible_travel_passing(self):
185    def test_history_impossible_travel_passing(self):
186        """Test history checks"""
187        Event.objects.create(
188            action=EventAction.LOGIN,
189            user=get_user(self.user),
190            context={
191                # Random location in Canada
192                "geo": {"lat": 55.868351, "long": -104.441011},
193            },
194        )
195        # Same location
196        self.request.context["geoip"] = {"lat": 55.868351, "long": -104.441011}
197
198        policy = GeoIPPolicy.objects.create(check_impossible_travel=True)
199
200        result: PolicyResult = policy.passes(self.request)
201        self.assertTrue(result.passing)

Test history checks

def test_history_no_geoip(self):
203    def test_history_no_geoip(self):
204        """Test history checks (previous login with no geoip data)"""
205        Event.objects.create(
206            action=EventAction.LOGIN,
207            user=get_user(self.user),
208            context={},
209        )
210        # Random location in Poland
211        self.request.context["geoip"] = {"lat": 50.950613, "long": 20.363679}
212
213        policy = GeoIPPolicy.objects.create(check_history_distance=True)
214
215        result: PolicyResult = policy.passes(self.request)
216        self.assertFalse(result.passing)

Test history checks (previous login with no geoip data)

def test_impossible_travel_no_geoip(self):
218    def test_impossible_travel_no_geoip(self):
219        """Test impossible travel checks (previous login with no geoip data)"""
220        Event.objects.create(
221            action=EventAction.LOGIN,
222            user=get_user(self.user),
223            context={},
224        )
225        # Random location in Poland
226        self.request.context["geoip"] = {"lat": 50.950613, "long": 20.363679}
227
228        policy = GeoIPPolicy.objects.create(check_impossible_travel=True)
229
230        result: PolicyResult = policy.passes(self.request)
231        self.assertFalse(result.passing)

Test impossible travel checks (previous login with no geoip data)