示例#1
0
def on_user_logged_out(sender, request: HttpRequest, user: User, **_):
    """Delete temporary user if the `delete_on_logout` flag is enabled"""
    if not user:
        return
    if "saml" in user.attributes:
        if "delete_on_logout" in user.attributes["saml"]:
            if user.attributes["saml"]["delete_on_logout"]:
                LOGGER.debug("Deleted temporary user", user=user)
                user.delete()
示例#2
0
 def ms_check_uac(self, attributes: dict[str, Any], user: User):
     """Check userAccountControl"""
     if "userAccountControl" not in attributes:
         return
     # Default from https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity
     #   /useraccountcontrol-manipulate-account-properties
     uac_bit = attributes.get("userAccountControl", 512)
     uac = UserAccountControl(uac_bit)
     user.is_active = UserAccountControl.ACCOUNTDISABLE not in uac
     user.save()
    def test_oauth_enroll(self):
        """test OAuth Source With With OIDC"""
        self.create_objects()
        self.driver.get(self.live_server_url)

        flow_executor = self.get_shadow_root("ak-flow-executor")
        identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
        wait = WebDriverWait(identification_stage, self.wait_timeout)

        wait.until(
            ec.presence_of_element_located(
                (By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
            )
        )
        identification_stage.find_element(
            By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
        ).click()

        # Now we should be at the IDP, wait for the login field
        self.wait.until(ec.presence_of_element_located((By.NAME, "username")))
        self.driver.find_element(By.NAME, "username").send_keys("example-user")
        self.driver.find_element(By.NAME, "username").send_keys(Keys.ENTER)
        sleep(2)

        # Wait until we're logged in
        self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "[name='confirm']")))
        self.driver.find_element(By.CSS_SELECTOR, "[name='confirm']").click()

        # Wait until we've loaded the user info page
        sleep(2)
        # Wait until we've logged in
        self.wait_for_url(self.if_user_url("/library"))
        self.driver.get(self.if_user_url("/settings"))

        self.assert_user(User(username="******", name="test name", email="*****@*****.**"))
    def test_oauth_enroll_auth(self):
        """test OAuth Source With With OIDC (enroll and authenticate again)"""
        self.test_oauth_enroll()
        # We're logged in at the end of this, log out and re-login
        self.driver.get(self.url("authentik_flows:default-invalidation"))
        sleep(1)
        flow_executor = self.get_shadow_root("ak-flow-executor")
        identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
        wait = WebDriverWait(identification_stage, self.wait_timeout)

        wait.until(
            ec.presence_of_element_located(
                (By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
            )
        )
        identification_stage.find_element(
            By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
        ).click()

        # Now we should be at the IDP, wait for the login field
        self.wait.until(ec.presence_of_element_located((By.ID, "login")))
        self.driver.find_element(By.ID, "login").send_keys("*****@*****.**")
        self.driver.find_element(By.ID, "password").send_keys("password")
        self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)

        # Wait until we're logged in
        self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "button[type=submit]")))
        self.driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()

        # Wait until we've logged in
        self.wait_for_url(self.if_user_url("/library"))
        self.driver.get(self.if_user_url("/settings"))

        self.assert_user(User(username="******", name="admin", email="*****@*****.**"))
示例#5
0
 def ms_check_pwd_last_set(self, attributes: dict[str, Any], user: User, created: bool):
     """Check pwdLastSet"""
     if "pwdLastSet" not in attributes:
         return
     pwd_last_set: datetime = attributes.get("pwdLastSet", datetime.now())
     pwd_last_set = pwd_last_set.replace(tzinfo=UTC)
     if created or pwd_last_set >= user.password_change_date:
         self.message(f"'{user.username}': Reset user's password")
         self._logger.debug(
             "Reset user's password",
             user=user.username,
             created=created,
             pwd_last_set=pwd_last_set,
         )
         user.set_unusable_password()
         user.save()
示例#6
0
    def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
        """Validate that user exists, and optionally their password"""
        uid_field = attrs["uid_field"]
        current_stage: IdentificationStage = self.stage.executor.current_stage

        pre_user = self.stage.get_user(uid_field)
        if not pre_user:
            with Hub.current.start_span(
                    op="authentik.stages.identification.validate_invalid_wait",
                    description="Sleep random time on invalid user identifier",
            ):
                # Sleep a random time (between 90 and 210ms) to "prevent" user enumeration attacks
                sleep(0.030 * SystemRandom().randint(3, 7))
            LOGGER.debug("invalid_login", identifier=uid_field)
            identification_failed.send(sender=self,
                                       request=self.stage.request,
                                       uid_field=uid_field)
            # We set the pending_user even on failure so it's part of the context, even
            # when the input is invalid
            # This is so its part of the current flow plan, and on flow restart can be kept, and
            # policies can be applied.
            self.stage.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = User(
                username=uid_field,
                email=uid_field,
            )
            if not current_stage.show_matched_user:
                self.stage.executor.plan.context[
                    PLAN_CONTEXT_PENDING_USER_IDENTIFIER] = uid_field
            raise ValidationError("Failed to authenticate.")
        self.pre_user = pre_user
        if not current_stage.password_stage:
            # No password stage select, don't validate the password
            return attrs

        password = attrs.get("password", None)
        if not password:
            LOGGER.warning("Password not set for ident+auth attempt")
        try:
            with Hub.current.start_span(
                    op="authentik.stages.identification.authenticate",
                    description="User authenticate call (combo stage)",
            ):
                user = authenticate(
                    self.stage.request,
                    current_stage.password_stage.backends,
                    username=self.pre_user.username,
                    password=password,
                )
            if not user:
                raise ValidationError("Failed to authenticate.")
            self.pre_user = user
        except PermissionDenied as exc:
            raise ValidationError(str(exc)) from exc
        return attrs
示例#7
0
    def test_oauth_enroll(self):
        """test OAuth Source With With OIDC"""
        self.create_objects()
        self.driver.get(self.live_server_url)

        flow_executor = self.get_shadow_root("ak-flow-executor")
        identification_stage = self.get_shadow_root(
            "ak-stage-identification", flow_executor
        )
        wait = WebDriverWait(identification_stage, self.wait_timeout)

        wait.until(
            ec.presence_of_element_located(
                (By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
            )
        )
        identification_stage.find_element(
            By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
        ).click()

        # Now we should be at the IDP, wait for the login field
        self.wait.until(ec.presence_of_element_located((By.ID, "login")))
        self.driver.find_element(By.ID, "login").send_keys("*****@*****.**")
        self.driver.find_element(By.ID, "password").send_keys("password")
        self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)

        # Wait until we're logged in
        self.wait.until(
            ec.presence_of_element_located((By.CSS_SELECTOR, "button[type=submit]"))
        )
        self.driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()

        # At this point we've been redirected back
        # and we're asked for the username
        flow_executor = self.get_shadow_root("ak-flow-executor")
        prompt_stage = self.get_shadow_root("ak-stage-prompt", flow_executor)

        prompt_stage.find_element(By.CSS_SELECTOR, "input[name=username]").click()
        prompt_stage.find_element(By.CSS_SELECTOR, "input[name=username]").send_keys(
            "foo"
        )
        prompt_stage.find_element(By.CSS_SELECTOR, "input[name=username]").send_keys(
            Keys.ENTER
        )

        # Wait until we've logged in
        self.wait_for_url(self.if_admin_url("/library"))
        self.driver.get(self.if_admin_url("/user"))

        self.assert_user(User(username="******", name="admin", email="*****@*****.**"))
示例#8
0
    def get_pending_user(self) -> User:
        """Either show the matched User object or show what the user entered,
        based on what the earlier stage (mostly IdentificationStage) set.
        _USER_IDENTIFIER overrides the first User, as PENDING_USER is used for
        other things besides the form display.

        If no user is pending, returns request.user"""
        if PLAN_CONTEXT_PENDING_USER_IDENTIFIER in self.executor.plan.context:
            return User(
                username=self.executor.plan.context.get(
                    PLAN_CONTEXT_PENDING_USER_IDENTIFIER),
                email="",
            )
        if PLAN_CONTEXT_PENDING_USER in self.executor.plan.context:
            return self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
        return self.request.user
示例#9
0
 def authenticate(
     self, request: HttpRequest, username: Optional[str], password: Optional[str], **kwargs: Any
 ) -> Optional[User]:
     try:
         user = User._default_manager.get_by_natural_key(username)
     except User.DoesNotExist:
         # Run the default password hasher once to reduce the timing
         # difference between an existing and a nonexistent user (#20760).
         User().set_password(password)
         return None
     tokens = Token.filter_not_expired(
         user=user, key=password, intent=TokenIntents.INTENT_APP_PASSWORD
     )
     if not tokens.exists():
         return None
     token = tokens.first()
     self.set_method("token", request, token=token)
     return token.user
示例#10
0
 def get(self, request: HttpRequest) -> HttpResponse:
     """Save data in the current flow to the currently pending user. If no user is pending,
     a new user is created."""
     if PLAN_CONTEXT_PROMPT not in self.executor.plan.context:
         message = _("No Pending data.")
         messages.error(request, message)
         LOGGER.debug(message)
         return self.executor.stage_invalid()
     data = self.executor.plan.context[PLAN_CONTEXT_PROMPT]
     user_created = False
     if PLAN_CONTEXT_PENDING_USER not in self.executor.plan.context:
         self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = User()
         self.executor.plan.context[
             PLAN_CONTEXT_AUTHENTICATION_BACKEND
         ] = class_to_path(ModelBackend)
         LOGGER.debug(
             "Created new user",
             flow_slug=self.executor.flow.slug,
         )
         user_created = True
     user: User = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
     # Before we change anything, check if the user is the same as in the request
     # and we're updating a password. In that case we need to update the session hash
     # Also check that we're not currently impersonating, so we don't update the session
     should_update_seesion = False
     if (
         any("password" in x for x in data.keys())
         and self.request.user.pk == user.pk
         and SESSION_IMPERSONATE_USER not in self.request.session
     ):
         should_update_seesion = True
     for key, value in data.items():
         setter_name = f"set_{key}"
         # Check if user has a setter for this key, like set_password
         if hasattr(user, setter_name):
             setter = getattr(user, setter_name)
             if callable(setter):
                 setter(value)
         # User has this key already
         elif hasattr(user, key):
             setattr(user, key, value)
         # Otherwise we just save it as custom attribute, but only if the value is prefixed with
         # `attribute_`, to prevent accidentally saving values
         else:
             if not key.startswith("attribute_"):
                 LOGGER.debug("discarding key", key=key)
                 continue
             user.attributes[key.replace("attribute_", "", 1)] = value
     # Extra check to prevent flows from saving a user with a blank username
     if user.username == "":
         LOGGER.warning("Aborting write to empty username", user=user)
         return self.executor.stage_invalid()
     # Check if we're writing from a source, and save the source to the attributes
     if PLAN_CONTEXT_SOURCES_CONNECTION in self.executor.plan.context:
         if USER_ATTRIBUTE_SOURCES not in user.attributes or not isinstance(
             user.attributes.get(USER_ATTRIBUTE_SOURCES), list
         ):
             user.attributes[USER_ATTRIBUTE_SOURCES] = []
         connection: UserSourceConnection = self.executor.plan.context[
             PLAN_CONTEXT_SOURCES_CONNECTION
         ]
         user.attributes[USER_ATTRIBUTE_SOURCES].append(connection.source.name)
     user.save()
     user_write.send(
         sender=self, request=request, user=user, data=data, created=user_created
     )
     # Check if the password has been updated, and update the session auth hash
     if should_update_seesion:
         update_session_auth_hash(self.request, user)
         LOGGER.debug("Updated session hash", user=user)
     LOGGER.debug(
         "Updated existing user",
         user=user,
         flow_slug=self.executor.flow.slug,
     )
     return self.executor.stage_ok()