예제 #1
0
    def passes(self, request: PolicyRequest) -> PolicyResult:
        if self.password_field not in request.context:
            LOGGER.warning(
                "Password field not set in Policy Request",
                field=self.password_field,
                fields=request.context.keys(),
            )
            return PolicyResult(False, _("Password not set in context"))
        password = request.context[self.password_field]

        filter_regex = []
        if self.amount_lowercase > 0:
            filter_regex.append(r"[a-z]{%d,}" % self.amount_lowercase)
        if self.amount_uppercase > 0:
            filter_regex.append(r"[A-Z]{%d,}" % self.amount_uppercase)
        if self.amount_symbols > 0:
            filter_regex.append(r"[%s]{%d,}" %
                                (self.symbol_charset, self.amount_symbols))
        full_regex = "|".join(filter_regex)
        LOGGER.debug("Built regex", regexp=full_regex)
        result = bool(re.compile(full_regex).match(password))

        result = result and len(password) >= self.length_min

        if not result:
            return PolicyResult(result, self.error_message)
        return PolicyResult(result)
예제 #2
0
 def passes(self, request: PolicyRequest) -> PolicyResult:
     """Check if request passes this PolicyBinding, check policy, group or user"""
     if self.policy:
         self.policy: Policy
         return self.policy.passes(request)
     if self.group:
         return PolicyResult(self.group.is_member(request.user))
     if self.user:
         return PolicyResult(request.user == self.user)
     return PolicyResult(False)
예제 #3
0
 def passes(self, request: PolicyRequest) -> PolicyResult:
     if "event" not in request.context:
         return PolicyResult(False)
     event: Event = request.context["event"]
     if event.action == self.action:
         return PolicyResult(True, "Action matched.")
     if event.client_ip == self.client_ip:
         return PolicyResult(True, "Client IP matched.")
     if event.app == self.app:
         return PolicyResult(True, "App matched.")
     return PolicyResult(False)
예제 #4
0
파일: models.py 프로젝트: eglia/authentik
 def passes(self, request: PolicyRequest) -> PolicyResult:
     """Check if request passes this PolicyBinding, check policy, group or user"""
     if self.policy:
         self.policy: Policy
         return self.policy.passes(request)
     if self.group:
         return PolicyResult(
             self.group.users.filter(pk=request.user.pk).exists())
     if self.user:
         return PolicyResult(request.user == self.user)
     return PolicyResult(False)
예제 #5
0
 def passes(self, request: PolicyRequest) -> PolicyResult:
     """If password change date is more than x days in the past, call set_unusable_password
     and show a notice"""
     actual_days = (now() - request.user.password_change_date).days
     days_since_expiry = (now() - (request.user.password_change_date +
                                   timedelta(days=self.days))).days
     if actual_days >= self.days:
         if not self.deny_only:
             request.user.set_unusable_password()
             request.user.save()
             message = _(("Password expired %(days)d days ago. "
                          "Please update your password.") %
                         {"days": days_since_expiry})
             return PolicyResult(False, message)
         return PolicyResult(False, _("Password has expired."))
     return PolicyResult(True)
예제 #6
0
 def run(self):  # pragma: no cover
     """Task wrapper to run policy checking"""
     try:
         self.connection.send(self.profiling_wrapper())
     except Exception as exc:  # pylint: disable=broad-except
         LOGGER.warning(str(exc))
         self.connection.send(PolicyResult(False, str(exc)))
예제 #7
0
 def execute(self) -> PolicyResult:
     """Run actual policy, returns result"""
     LOGGER.debug(
         "P_ENG(proc): Running policy",
         policy=self.binding.policy,
         user=self.request.user,
         process="PolicyProcess",
     )
     try:
         policy_result = self.binding.passes(self.request)
         # Invert result if policy.negate is set
         if self.binding.negate:
             policy_result.passing = not policy_result.passing
         if self.binding.policy and not self.request.debug:
             if self.binding.policy.execution_logging:
                 self.create_event(
                     EventAction.POLICY_EXECUTION,
                     message="Policy Execution",
                     result=policy_result,
                 )
     except PolicyException as exc:
         # Either use passed original exception or whatever we have
         src_exc = exc.src_exc if exc.src_exc else exc
         error_string = (
             TRACEBACK_HEADER
             + "".join(format_tb(src_exc.__traceback__))
             + str(src_exc)
         )
         # Create policy exception event, only when we're not debugging
         if not self.request.debug:
             self.create_event(EventAction.POLICY_EXCEPTION, message=error_string)
         LOGGER.debug("P_ENG(proc): error", exc=src_exc)
         policy_result = PolicyResult(False, str(src_exc))
     policy_result.source_binding = self.binding
     if not self.request.debug:
         key = cache_key(self.binding, self.request)
         cache.set(key, policy_result)
     LOGGER.debug(
         "P_ENG(proc): finished and cached ",
         policy=self.binding.policy,
         result=policy_result,
         process="PolicyProcess",
         passing=policy_result.passing,
         user=self.request.user,
     )
     return policy_result
예제 #8
0
 def check_access(self, request: Request, slug: str) -> Response:
     """Check access to a single application by slug"""
     # Don't use self.get_object as that checks for view_application permission
     # which the user might not have, even if they have access
     application = get_object_or_404(Application, slug=slug)
     # If the current user is superuser, they can set `for_user`
     for_user = self.request.user
     if self.request.user.is_superuser and "for_user" in request.data:
         for_user = get_object_or_404(User, pk=request.data.get("for_user"))
     engine = PolicyEngine(application, for_user, self.request)
     engine.build()
     result = engine.result
     response = PolicyTestResultSerializer(PolicyResult(False))
     if result.passing:
         response = PolicyTestResultSerializer(PolicyResult(True))
     if self.request.user.is_superuser:
         response = PolicyTestResultSerializer(result)
     return Response(response.data)
예제 #9
0
파일: models.py 프로젝트: eglia/authentik
 def passes(self, request: PolicyRequest) -> PolicyResult:
     remote_ip = get_client_ip(request.http_request)
     passing = True
     if self.check_ip:
         score = cache.get_or_set(CACHE_KEY_IP_PREFIX + remote_ip, 0)
         passing = passing and score <= self.threshold
     if self.check_username:
         score = cache.get_or_set(CACHE_KEY_USER_PREFIX + request.user.username, 0)
         passing = passing and score <= self.threshold
     return PolicyResult(passing)
예제 #10
0
 def result(self) -> PolicyResult:
     """Get policy-checking result"""
     process_results: list[PolicyResult] = [
         x.result for x in self.__processes if x.result
     ]
     all_results = list(process_results + self.__cached_policies)
     if len(all_results) < self.__expected_result_count:  # pragma: no cover
         raise AssertionError("Got less results than polices")
     # No results, no policies attached -> passing
     if len(all_results) == 0:
         return PolicyResult(self.empty_result)
     passing = False
     if self.mode == PolicyEngineMode.MODE_ALL:
         passing = all(x.passing for x in all_results)
     if self.mode == PolicyEngineMode.MODE_ANY:
         passing = any(x.passing for x in all_results)
     result = PolicyResult(passing)
     result.source_results = all_results
     result.messages = tuple(y for x in all_results for y in x.messages)
     return result
예제 #11
0
 def run(self):  # pragma: no cover
     """Task wrapper to run policy checking"""
     with Hub.current.start_span(op="policy.process.execute", ) as span:
         span: Span
         span.set_data("policy", self.binding.policy)
         span.set_data("request", self.request)
         try:
             self.connection.send(self.execute())
         except Exception as exc:  # pylint: disable=broad-except
             LOGGER.warning(str(exc))
             self.connection.send(PolicyResult(False, str(exc)))
예제 #12
0
 def evaluate(self, expression_source: str) -> PolicyResult:
     """Parse and evaluate expression. Policy is expected to return a truthy object.
     Messages can be added using 'do ak_message()'."""
     try:
         result = super().evaluate(expression_source)
     except PolicyException as exc:
         # PolicyExceptions should be propagated back to the process,
         # which handles recording and returning a correct result
         raise exc
     except Exception as exc:  # pylint: disable=broad-except
         LOGGER.warning("Expression error", exc=exc)
         return PolicyResult(False, str(exc))
     else:
         policy_result = PolicyResult(False, *self._messages)
         if result is None:
             LOGGER.warning(
                 "Expression policy returned None",
                 src=expression_source,
                 req=self._context,
             )
             policy_result.passing = False
         if result:
             policy_result.passing = bool(result)
         return policy_result
예제 #13
0
 def passes(self, request: PolicyRequest) -> PolicyResult:
     remote_ip = get_client_ip(request.http_request)
     query = Q()
     if self.check_ip:
         query |= Q(ip=remote_ip)
     if self.check_username:
         query |= Q(identifier=request.user.username)
     score = (Reputation.objects.filter(query).aggregate(
         total_score=Sum("score"))["total_score"] or 0)
     passing = score <= self.threshold
     LOGGER.debug(
         "Score for user",
         username=request.user.username,
         remote_ip=remote_ip,
         score=score,
         passing=passing,
     )
     return PolicyResult(bool(passing))
예제 #14
0
    def passes(self, request: PolicyRequest) -> PolicyResult:
        if (self.password_field not in request.context
                and self.password_field not in request.context.get(
                    PLAN_CONTEXT_PROMPT, {})):
            LOGGER.warning(
                "Password field not set in Policy Request",
                field=self.password_field,
                fields=request.context.keys(),
                prompt_fields=request.context.get(PLAN_CONTEXT_PROMPT,
                                                  {}).keys(),
            )
            return PolicyResult(False, _("Password not set in context"))

        if self.password_field in request.context:
            password = request.context[self.password_field]
        else:
            password = request.context[PLAN_CONTEXT_PROMPT][
                self.password_field]

        if len(password) < self.length_min:
            LOGGER.debug("password failed", reason="length")
            return PolicyResult(False, self.error_message)

        if self.amount_digits > 0 and len(
                RE_DIGITS.findall(password)) < self.amount_digits:
            LOGGER.debug("password failed", reason="amount_digits")
            return PolicyResult(False, self.error_message)
        if self.amount_lowercase > 0 and len(
                RE_LOWER.findall(password)) < self.amount_lowercase:
            LOGGER.debug("password failed", reason="amount_lowercase")
            return PolicyResult(False, self.error_message)
        if self.amount_uppercase > 0 and len(
                RE_UPPER.findall(password)) < self.amount_lowercase:
            LOGGER.debug("password failed", reason="amount_uppercase")
            return PolicyResult(False, self.error_message)
        if self.amount_symbols > 0:
            count = 0
            for symbol in self.symbol_charset:
                count += password.count(symbol)
            if count < self.amount_symbols:
                LOGGER.debug("password failed", reason="amount_symbols")
                return PolicyResult(False, self.error_message)

        return PolicyResult(True)
예제 #15
0
 def run(self):  # pragma: no cover
     """Task wrapper to run policy checking"""
     with Hub.current.start_span(
         op="policy.process.execute",
     ) as span, HIST_POLICIES_EXECUTION_TIME.labels(
         binding_order=self.binding.order,
         binding_target_type=self.binding.target_type,
         binding_target_name=self.binding.target_name,
         object_name=self.request.obj,
         object_type=f"{self.request.obj._meta.app_label}.{self.request.obj._meta.model_name}",
         user=str(self.request.user),
     ).time():
         span: Span
         span.set_data("policy", self.binding.policy)
         span.set_data("request", self.request)
         try:
             self.connection.send(self.execute())
         except Exception as exc:  # pylint: disable=broad-except
             LOGGER.warning(str(exc))
             self.connection.send(PolicyResult(False, str(exc)))
예제 #16
0
 def dispatch(self, request: HttpRequest, *args: Any,
              **kwargs: Any) -> HttpResponse:
     try:
         self.pre_permission_check()
     except RequestValidationError as exc:
         if exc.response:
             return exc.response
         return self.handle_no_permission()
     try:
         self.resolve_provider_application()
     except (Application.DoesNotExist, Provider.DoesNotExist) as exc:
         LOGGER.warning("failed to resolve application", exc=exc)
         return self.handle_no_permission_authenticated(
             PolicyResult(False, _("Failed to resolve application")))
     # Check if user is unauthenticated, so we pass the application
     # for the identification stage
     if not request.user.is_authenticated:
         LOGGER.warning("user not authenticated")
         return self.handle_no_permission()
     # Check permissions
     result = self.user_has_access()
     if not result.passing:
         return self.handle_no_permission_authenticated(result)
     return super().dispatch(request, *args, **kwargs)
예제 #17
0
from authentik.flows.markers import ReevaluateMarker, StageMarker
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding, InvalidResponseAction
from authentik.flows.planner import FlowPlan, FlowPlanner
from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER, StageView
from authentik.flows.tests import FlowTestCase
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_PLAN, FlowExecutorView
from authentik.lib.config import CONFIG
from authentik.policies.dummy.models import DummyPolicy
from authentik.policies.models import PolicyBinding
from authentik.policies.reputation.models import ReputationPolicy
from authentik.policies.types import PolicyResult
from authentik.stages.deny.models import DenyStage
from authentik.stages.dummy.models import DummyStage
from authentik.stages.identification.models import IdentificationStage, UserFields

POLICY_RETURN_FALSE = PropertyMock(return_value=PolicyResult(False))
POLICY_RETURN_TRUE = MagicMock(return_value=PolicyResult(True))


def to_stage_response(request: HttpRequest, source: HttpResponse):
    """Mock for to_stage_response that returns the original response, so we can check
    inheritance and member attributes"""
    return source


TO_STAGE_RESPONSE_MOCK = MagicMock(side_effect=to_stage_response)


class TestFlowExecutor(FlowTestCase):
    """Test executor"""
    def setUp(self):
예제 #18
0
 def passes(self, request: PolicyRequest) -> PolicyResult:
     """Wait random time then return result"""
     wait = SystemRandom().randrange(self.wait_min, self.wait_max)
     LOGGER.debug("Policy waiting", policy=self, delay=wait)
     sleep(wait)
     return PolicyResult(self.result, "dummy")