Merge branch '1-suspicious-request' into 'master'

Resolve "Suspicious request detector (many invalid logins from one IP, many attempts on one username, etc)"

Closes #1

See merge request BeryJu.org/passbook!3
This commit is contained in:
Jens Langhammer
2019-03-03 20:04:56 +00:00
23 changed files with 253 additions and 61 deletions

View File

@ -65,7 +65,7 @@ class AuthenticationView(UserPassesTestMixin, View):
self.pending_factors = []
for factor in _all_factors:
policy_engine = PolicyEngine(factor.policies.all())
policy_engine.for_user(self.pending_user)
policy_engine.for_user(self.pending_user).with_request(request).build()
if policy_engine.result[0]:
self.pending_factors.append((factor.uuid.hex, factor.type))
# Read and instantiate factor from session

View File

@ -153,7 +153,7 @@ class Application(PolicyModel):
def user_is_authorized(self, user: User) -> bool:
"""Check if user is authorized to use this application"""
from passbook.core.policies import PolicyEngine
return PolicyEngine(self.policies.all()).for_user(user).result
return PolicyEngine(self.policies.all()).for_user(user).build().result
def get_provider(self):
"""Get casted provider instance"""

View File

@ -2,6 +2,7 @@
from logging import getLogger
from celery import group
from ipware import get_client_ip
from passbook.core.celery import CELERY_APP
from passbook.core.models import Policy, User
@ -33,18 +34,36 @@ class PolicyEngine:
policies = None
_group = None
_request = None
_user = None
def __init__(self, policies):
self.policies = policies
self._request = None
self._user = None
def for_user(self, user):
"""Check policies for user"""
self._user = user
return self
def with_request(self, request):
"""Set request"""
self._request = request
return self
def build(self):
"""Build task group"""
signatures = []
kwargs = {
'__password__': getattr(user, '__password__', None)
'__password__': getattr(self._user, '__password__', None),
}
if self._request:
kwargs['remote_ip'], _ = get_client_ip(self._request)
if not kwargs['remote_ip']:
kwargs['remote_ip'] = '255.255.255.255'
for policy in self.policies:
signatures.append(_policy_engine_task.s(user.pk, policy.pk.hex, **kwargs))
signatures.append(_policy_engine_task.s(self._user.pk, policy.pk.hex, **kwargs))
self._group = group(signatures)()
return self

View File

@ -1,5 +1,6 @@
django>=2.0
django-model-utils
django-ipware
djangorestframework
PyYAML
raven

View File

@ -77,6 +77,7 @@ INSTALLED_APPS = [
'passbook.hibp_policy.apps.PassbookHIBPConfig',
'passbook.pretend.apps.PassbookPretendConfig',
'passbook.password_expiry_policy.apps.PassbookPasswordExpiryPolicyConfig',
'passbook.suspicious_policy.apps.PassbookSuspiciousPolicyConfig',
]
# Message Tag fix for bootstrap CSS Classes

View File

@ -20,7 +20,7 @@ def password_policy_checker(sender, password, **kwargs):
_all_factors = PasswordFactor.objects.filter(enabled=True).order_by('order')
for factor in _all_factors:
policy_engine = PolicyEngine(factor.password_policies.all().select_subclasses())
policy_engine.for_user(sender)
policy_engine.for_user(sender).build()
passing, messages = policy_engine.result
if not passing:
raise PasswordPolicyInvalid(*messages)

View File

@ -16,7 +16,7 @@ def user_factors(context):
for factor in _all_factors:
_link = factor.has_user_settings()
policy_engine = PolicyEngine(factor.policies.all())
policy_engine.for_user(user)
policy_engine.for_user(user).with_request(context.get('request')).build()
if policy_engine.result[0] and _link:
matching_factors.append(_link)
return matching_factors