예제 #1
0
    def get_jwt_keys(self) -> Union[RSAPrivateKey, str]:
        """
        Takes a provider and returns the set of keys associated with it.
        Returns a list of keys.
        """
        if self.jwt_alg == JWTAlgorithms.RS256:
            # if the user selected RS256 but didn't select a
            # CertificateKeyPair, we fall back to HS256
            if not self.rsa_key:
                Event.new(
                    EventAction.CONFIGURATION_ERROR,
                    provider=self,
                    message=
                    "Provider was configured for RS256, but no key was selected.",
                ).save()
                self.jwt_alg = JWTAlgorithms.HS256
                self.save()
            else:
                return self.rsa_key.private_key

        if self.jwt_alg == JWTAlgorithms.HS256:
            return self.client_secret

        raise Exception("Unsupported key algorithm.")
예제 #2
0
    def check_saml_request(self) -> Optional[HttpRequest]:
        """Handle REDIRECT bindings"""
        if REQUEST_KEY_SAML_REQUEST not in self.request.GET:
            LOGGER.info("handle_saml_request: SAML payload missing")
            return bad_request_message(self.request,
                                       "The SAML request payload is missing.")

        try:
            auth_n_request = AuthNRequestParser(self.provider).parse_detached(
                self.request.GET[REQUEST_KEY_SAML_REQUEST],
                self.request.GET.get(REQUEST_KEY_RELAY_STATE),
                self.request.GET.get(REQUEST_KEY_SAML_SIGNATURE),
                self.request.GET.get(REQUEST_KEY_SAML_SIG_ALG),
            )
            self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request
        except CannotHandleAssertion as exc:
            Event.new(
                EventAction.CONFIGURATION_ERROR,
                provider=self.provider,
                message=str(exc),
            ).save()
            LOGGER.info(str(exc))
            return bad_request_message(self.request, str(exc))
        return None
예제 #3
0
def update_latest_version(self: MonitoredTask):
    """Update latest version info"""
    if CONFIG.y_bool("disable_update_check"):
        cache.set(VERSION_CACHE_KEY, "0.0.0", VERSION_CACHE_TIMEOUT)
        self.set_status(
            TaskResult(TaskResultStatus.WARNING,
                       messages=["Version check disabled."]))
        return
    try:
        response = get_http_session().get(
            "https://version.goauthentik.io/version.json", )
        response.raise_for_status()
        data = response.json()
        upstream_version = data.get("stable", {}).get("version")
        cache.set(VERSION_CACHE_KEY, upstream_version, VERSION_CACHE_TIMEOUT)
        self.set_status(
            TaskResult(TaskResultStatus.SUCCESSFUL,
                       ["Successfully updated latest Version"]))
        _set_prom_info()
        # Check if upstream version is newer than what we're running,
        # and if no event exists yet, create one.
        if LOCAL_VERSION < parse(upstream_version):
            # Event has already been created, don't create duplicate
            if Event.objects.filter(
                    action=EventAction.UPDATE_AVAILABLE,
                    context__new_version=upstream_version,
            ).exists():
                return
            event_dict = {"new_version": upstream_version}
            if match := re.search(URL_FINDER,
                                  data.get("stable", {}).get("changelog", "")):
                event_dict["message"] = f"Changelog: {match.group()}"
            Event.new(EventAction.UPDATE_AVAILABLE, **event_dict).save()
    except (RequestException, IndexError) as exc:
        cache.set(VERSION_CACHE_KEY, "0.0.0", VERSION_CACHE_TIMEOUT)
        self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
예제 #4
0
 def create_event(self, action: str, message: str, **kwargs):
     """Create event with common values from `self.request` and `self.binding`."""
     # Keep a reference to http_request even if its None, because cleanse_dict will remove it
     http_request = self.request.http_request
     event = Event.new(
         action=action,
         message=message,
         policy_uuid=self.binding.policy.policy_uuid.hex,
         binding=self.binding,
         request=self.request,
         **kwargs,
     )
     event.set_user(self.request.user)
     if http_request:
         event.from_http(http_request)
     else:
         event.save()
예제 #5
0
 def test_event_retention(self):
     """Test tenant's event retention"""
     tenant = Tenant.objects.create(
         domain="foo",
         default=True,
         branding_title="custom",
         event_retention="weeks=3",
     )
     factory = RequestFactory()
     request = factory.get("/")
     request.tenant = tenant
     event = Event.new(action=EventAction.SYSTEM_EXCEPTION, message="test").from_http(request)
     self.assertEqual(event.expires.day, (event.created + timedelta_from_string("weeks=3")).day)
     self.assertEqual(
         event.expires.month,
         (event.created + timedelta_from_string("weeks=3")).month,
     )
     self.assertEqual(
         event.expires.year, (event.created + timedelta_from_string("weeks=3")).year
     )
예제 #6
0
 def test(self, request: Request, pk=None) -> Response:
     """Send example notification using selected transport. Requires
     Modify permissions."""
     transport: NotificationTransport = self.get_object()
     notification = Notification(
         severity=NotificationSeverity.NOTICE,
         body=f"Test Notification from transport {transport.name}",
         user=request.user,
         event=Event(
             action="Test",
             user=get_user(request.user),
             app=self.__class__.__module__,
             context={"foo": "bar"},
         ),
     )
     try:
         response = NotificationTransportTestSerializer(
             data={"messages": transport.send(notification)})
         response.is_valid()
         return Response(response.data)
     except NotificationTransportError as exc:
         return Response(str(exc.__cause__ or None), status=500)
예제 #7
0
 def sync(self) -> int:
     """Iterate over all LDAP Users and create authentik_core.User instances"""
     if not self._source.sync_users:
         self._logger.warning("User syncing is disabled for this Source")
         return -1
     users = self._source.connection.extend.standard.paged_search(
         search_base=self.base_dn_users,
         search_filter=self._source.user_object_filter,
         search_scope=ldap3.SUBTREE,
         attributes=[
             ldap3.ALL_ATTRIBUTES, ldap3.ALL_OPERATIONAL_ATTRIBUTES
         ],
     )
     user_count = 0
     for user in users:
         attributes = user.get("attributes", {})
         user_dn = self._flatten(user.get("entryDN", user.get("dn")))
         if self._source.object_uniqueness_field not in attributes:
             self._logger.warning(
                 "Cannot find uniqueness Field in attributes",
                 attributes=attributes.keys(),
                 dn=user_dn,
             )
             continue
         uniq = self._flatten(
             attributes[self._source.object_uniqueness_field])
         try:
             defaults = self.build_user_properties(user_dn, **attributes)
             self._logger.debug("Creating user with attributes", **defaults)
             if "username" not in defaults:
                 raise IntegrityError(
                     "Username was not set by propertymappings")
             ak_user, created = User.objects.update_or_create(
                 **{
                     f"attributes__{LDAP_UNIQUENESS}": uniq,
                     "defaults": defaults,
                 })
         except (IntegrityError, FieldError) as exc:
             Event.new(
                 EventAction.CONFIGURATION_ERROR,
                 message=(
                     f"Failed to create user: {str(exc)} "
                     "To merge new user with existing user, set the user's "
                     f"Attribute '{LDAP_UNIQUENESS}' to '{uniq}'"),
                 source=self._source,
                 dn=user_dn,
             ).save()
         else:
             self._logger.debug("Synced User",
                                user=ak_user.username,
                                created=created)
             user_count += 1
             pwd_last_set: datetime = attributes.get(
                 "pwdLastSet", datetime.now())
             pwd_last_set = pwd_last_set.replace(tzinfo=UTC)
             if created or pwd_last_set >= ak_user.password_change_date:
                 self._logger.debug(
                     "Reset user's password",
                     user=ak_user.username,
                     created=created,
                     pwd_last_set=pwd_last_set,
                 )
                 ak_user.set_unusable_password()
                 ak_user.save()
     return user_count
예제 #8
0
def pre_delete_event(sender, instance: TOTPDevice, **_):
    """Create event before deleting TOTP Devices"""
    # Create event with email notification
    event = Event.new("totp_disable", message="User disabled Time-based OTP.")
    event.set_user(instance.user)
    event.save()
예제 #9
0
 def run(self):
     Event.new(self.action, **self.kwargs).from_http(self.request,
                                                     user=self.user)
예제 #10
0
 def sync(self) -> int:
     """Iterate over all LDAP Groups and create authentik_core.Group instances"""
     if not self._source.sync_groups:
         self.message("Group syncing is disabled for this Source")
         return -1
     groups = self._source.connection.extend.standard.paged_search(
         search_base=self.base_dn_groups,
         search_filter=self._source.group_object_filter,
         search_scope=ldap3.SUBTREE,
         attributes=[
             ldap3.ALL_ATTRIBUTES, ldap3.ALL_OPERATIONAL_ATTRIBUTES
         ],
     )
     group_count = 0
     for group in groups:
         attributes = group.get("attributes", {})
         group_dn = self._flatten(
             self._flatten(group.get("entryDN", group.get("dn"))))
         if self._source.object_uniqueness_field not in attributes:
             self.message(
                 f"Cannot find uniqueness field in attributes: '{group_dn}'",
                 attributes=attributes.keys(),
                 dn=group_dn,
             )
             continue
         uniq = self._flatten(
             attributes[self._source.object_uniqueness_field])
         try:
             defaults = self.build_group_properties(group_dn, **attributes)
             self._logger.debug("Creating group with attributes",
                                **defaults)
             if "name" not in defaults:
                 raise IntegrityError(
                     "Name was not set by propertymappings")
             # Special check for `users` field, as this is an M2M relation, and cannot be sync'd
             if "users" in defaults:
                 del defaults["users"]
             ak_group, created = self.update_or_create_attributes(
                 Group,
                 {
                     f"attributes__{LDAP_UNIQUENESS}": uniq,
                     "parent": self._source.sync_parent_group,
                 },
                 defaults,
             )
         except (IntegrityError, FieldError, TypeError) as exc:
             Event.new(
                 EventAction.CONFIGURATION_ERROR,
                 message=
                 (f"Failed to create group: {str(exc)} "
                  "To merge new group with existing group, set the groups's "
                  f"Attribute '{LDAP_UNIQUENESS}' to '{uniq}'"),
                 source=self._source,
                 dn=group_dn,
             ).save()
         else:
             self._logger.debug("Synced group",
                                group=ak_group.name,
                                created=created)
             group_count += 1
     return group_count
예제 #11
0
 def view_key(self, request: Request, identifier: str) -> Response:
     """Return token key and log access"""
     token: Token = self.get_object()
     Event.new(EventAction.SECRET_VIEW,
               secret=token).from_http(request)  # noqa # nosec
     return Response(TokenViewSerializer({"key": token.key}).data)