diff --git a/README.md b/README.md
index 81d8ee167478098db3e4b05643b5fb2e0faacacf..709800e43cb53635da90d5936683d6e1bcec3e02 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,8 @@ Any change that significantly changes behavior in a backward-incompatible way or
 - Auth Process filters:
   - computeloa
   - iscesneteligible
+  - iseinfraczeligible
+  - iseinfraassured
 
 ## Instalation
 
diff --git a/composer.json b/composer.json
index 8beedd642b2b989299eacbe657c76fa5bd1b712c..2e3d0a6a558fc83088cdf8442c7a15088411243b 100644
--- a/composer.json
+++ b/composer.json
@@ -17,7 +17,8 @@
     "ext-curl": "*"
   },
   "suggest": {
-    "cesnet/simplesamlphp-module-privacyidea": "included privacyIDEA template is for this module"
+    "cesnet/simplesamlphp-module-privacyidea": "included privacyIDEA template is for this module",
+    "ext-yaml": "needed to run IsEinfraCZEligible auth filter"
   },
   "config": {
     "platform": {
diff --git a/config-templates/processFilterConfigurations-example.md b/config-templates/processFilterConfigurations-example.md
index 4abe073f3b72f57565696c87035ca69434ad5951..7fac57ab7810fe878da23b0d1efb0ff071a74590 100644
--- a/config-templates/processFilterConfigurations-example.md
+++ b/config-templates/processFilterConfigurations-example.md
@@ -27,3 +27,40 @@ Example how to configure IsCesnetEligible filter:
       'LDAP.attributeName' => 'isCesnetEligible',
   ],
   ```
+
+## IsEinfraCZEligible
+
+Example how to configure IsEinfraCZEligible filter:
+
+- Configuration file is saml20-idp-hosted.php
+- User eligibilites and affiliations should be retrieved before this filter is run
+- User ext. sources are not updated as part of this filter
+
+  ```php
+  33 => [
+      'class' => 'cesnet:IsEinfraCZEligible',
+      'userEligibilityAttrName' => 'session_eligibilities',
+      'userAffiliationAttrName' => 'externalAffiliation',
+      'eligibilitiesAttrKey' => 'einfracz',
+  ],
+  ```
+
+## IsEinfraAssured
+
+Example how to configure IsEinfraAssured filter:
+
+- Configuration file is saml20-idp-hosted.php
+- Einfracz eligibility should be resolved before this filter is run
+- User eligibilities and session eligibilities should be retrieved before this filter is run
+- The result values are stored in attributes defined in `userAssuranceAttrNames` as value `assurancePrefix-1y`
+
+  ```php
+  34 => [
+      'class' => 'cesnet:IsEinfraAssured',
+      'userAssuranceAttrNames' => ['eduPersonAssurance'],
+      'userEligibilitiesAttrName' => 'eligibilities',
+      'sessionEligibilitiesAttrName' => 'session_eligibilities'
+      'eligibilitiesAttrKey' => 'einfracz',
+      'assurancePrefix' => 'assuranceprefix',
+  ],
+  ```
diff --git a/lib/Auth/Process/IsEinfraAssured.php b/lib/Auth/Process/IsEinfraAssured.php
new file mode 100644
index 0000000000000000000000000000000000000000..c3403008f2a0a791943449569e3b37722b638e00
--- /dev/null
+++ b/lib/Auth/Process/IsEinfraAssured.php
@@ -0,0 +1,98 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\cesnet\Auth\Process;
+
+use SimpleSAML\Auth\ProcessingFilter;
+use SimpleSAML\Configuration;
+use SimpleSAML\Logger;
+use SimpleSAML\Error\Exception;
+
+/**
+ * Class IsEinfraAssured.
+ *
+ * This class puts assurance timestamp as {prefix}-1y representing
+ * user's einfra eligibility is not older than 1 year.
+ */
+class IsEinfraAssured extends ProcessingFilter
+{
+    public const LOGGER_PREFIX = 'cesnet:IsEinfraAssured - ';
+
+    public const USER_ASSURANCE_ATTRS = 'userAssuranceAttrNames';
+
+    public const USER_ELIGIBILITIES_ATTR = 'userEligibilitiesAttrName';
+
+    public const SESSION_ELIGIBILITIES_ATTR = 'sessionEligibilitiesAttrName';
+
+    public const ELIGIBILITIES_ATTR_KEY = 'eligibilitiesAttrKey';
+
+    public const PREFIX = 'assurancePrefix';
+
+    private $userEligibilitiesAttr = 'eligibilities';
+
+    private $sessionEligibilitiesAttr = 'session_eligibilities';
+
+    private $assuranceAttrs = ['eduPersonAssurance'];
+
+    private $prefix = 'prefix';
+
+    private const SUFFIX = '-1y';
+
+    private $eligibilityKey = 'einfracz';
+
+    public function __construct($config, $reserved)
+    {
+        parent::__construct($config, $reserved);
+        $config = Configuration::loadFromArray($config);
+
+        if ($config === null) {
+            throw new Exception(
+                self::LOGGER_PREFIX . ' configuration is missing or invalid!'
+            );
+        }
+
+        $this->assuranceAttrs = $config->getArray(self::USER_ASSURANCE_ATTRS, $this->assuranceAttrs);
+        $this->userEligibilitiesAttr = $config->getString(
+            self::USER_ELIGIBILITIES_ATTR,
+            $this->userEligibilitiesAttr
+        );
+        $this->sessionEligibilitiesAttr = $config->getString(
+            self::SESSION_ELIGIBILITIES_ATTR,
+            $this->sessionEligibilitiesAttr
+        );
+        $this->prefix = $config->getString(self::PREFIX, $this->prefix);
+        $this->eligibilityKey = $config->getString(self::ELIGIBILITIES_ATTR_KEY, $this->eligibilityKey);
+
+        if (empty($this->assuranceAttrs)) {
+            throw new Exception(
+                self::LOGGER_PREFIX . ' empty array detected !'
+            );
+        }
+    }
+
+    public function process(&$request)
+    {
+        $timestamp = 0;
+        if (isset($request['Attributes'][$this->sessionEligibilitiesAttr][$this->eligibilityKey])) {
+            $timestamp = $request['Attributes'][$this->sessionEligibilitiesAttr][$this->eligibilityKey];
+        } elseif (isset($request['Attributes'][$this->userEligibilitiesAttr][$this->eligibilityKey])) {
+            $timestamp = $request['Attributes'][$this->userEligibilitiesAttr][$this->eligibilityKey];
+        }
+
+        if ($timestamp > strtotime('-1 year')) {
+            foreach ($this->assuranceAttrs as $attr) {
+                $request['Attributes'][$attr][] = $this->prefix . self::SUFFIX;
+            }
+            Logger::debug(
+                self::LOGGER_PREFIX .
+                'Added assurance timestamp ' . $this->prefix . self::SUFFIX
+            );
+        } else {
+            Logger::debug(
+                self::LOGGER_PREFIX .
+                'User is not assured - assurance timestamp not added.'
+            );
+        }
+    }
+}
diff --git a/lib/Auth/Process/IsEinfraCZEligible.php b/lib/Auth/Process/IsEinfraCZEligible.php
new file mode 100644
index 0000000000000000000000000000000000000000..8ca1cf063e7cd760ae019e6e2003cf3fad310291
--- /dev/null
+++ b/lib/Auth/Process/IsEinfraCZEligible.php
@@ -0,0 +1,180 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\cesnet\Auth\Process;
+
+use SimpleSAML\Auth\ProcessingFilter;
+use SimpleSAML\Configuration;
+use SimpleSAML\Error\Exception;
+use SimpleSAML\Logger;
+
+/**
+ * Class IsEinfraCZEligible.
+ *
+ * This class puts the current timestamp to attribute storing user eligibilities,
+ * when user's affiliations are sufficient to be eligible, which is checked with
+ * a configuration that is periodically fetched from configured URL.
+ */
+class IsEinfraCZEligible extends ProcessingFilter
+{
+    public const LOGGER_PREFIX = 'cesnet:IsEinfraCZEligible - ';
+
+    public const IDP_ELIGIBLE_AFFILIATIONS_LIST_LINK =
+        'https://gitlab.ics.muni.cz/perun-proxy-aai/e-infracz_idps_data/-/raw/main/einfra_isacademic/result.yaml';
+
+    public const LINK_REFRESH_RATE_MINS = 15; // older file will be refreshed
+
+    public const FILE_MAX_AGE_HOURS = 24; // older file won't be considered valid
+
+    public const DATA_FILEPATH = '/tmp/einfra_isacademic_eligible.yml'; // where file will be stored and loaded from
+
+    public const USER_ELIGIBILITY_ATTR = 'userEligibilityAttrName';
+
+    public const USER_AFFILIATIONS_ATTR = 'userAffiliationAttrName';
+
+    public const USER_ELIGIBILITY_KEY = 'eligibilitiesAttrKey';
+
+    private $userAffiliationAttr = 'externalAffiliation';
+
+    private $userEligibilityAttr = 'session_eligibilities';
+
+    private $userEligibilityAttrKey = 'einfracz';
+
+
+    public function __construct($config, $reserved)
+    {
+        parent::__construct($config, $reserved);
+        $config = Configuration::loadFromArray($config);
+
+        if ($config === null) {
+            throw new Exception(
+                self::LOGGER_PREFIX . ' configuration is missing or invalid!'
+            );
+        }
+
+        $this->userEligibilityAttr = $config->getString(self::USER_ELIGIBILITY_ATTR, $this->userEligibilityAttr);
+        $this->userAffiliationAttr = $config->getString(self::USER_AFFILIATIONS_ATTR, $this->userAffiliationAttr);
+        $this->userEligibilityAttrKey = $config->getString(self::USER_ELIGIBILITY_KEY, $this->userEligibilityAttrKey);
+    }
+
+    public function process(&$request)
+    {
+        $userScopedAffiliations = [];
+        if (isset($request['Attributes'][$this->userAffiliationAttr])) {
+            $userScopedAffiliations
+                = $request['Attributes'][$this->userAffiliationAttr];
+        } else {
+            Logger::error(
+                self::LOGGER_PREFIX .
+                'Attribute with name \'' . $this->userAffiliationAttr . '\' was not received from IdP!'
+            );
+        }
+
+        $isEligible = $this->isEligible($request['saml:sp:IdP'], $userScopedAffiliations);
+
+        Logger::debug(
+            self::LOGGER_PREFIX . 'User is' . ($isEligible ? ' ' : ' not ') . 'eligible'
+        );
+
+        if ($isEligible) {
+            $request['Attributes'][$this->userEligibilityAttr][$this->userEligibilityAttrKey] = strval(time());
+        }
+        Logger::debug(
+            self::LOGGER_PREFIX . 'Result eligibilities attribute: ' .
+            json_encode($request['Attributes'][$this->userEligibilityAttr])
+        );
+    }
+
+    private function isEligible(string $idpEntityId, array $userScopedAffiliations): bool
+    {
+        $userAffiliations = array_map(
+            function ($item) {
+                return explode("@", $item)[0];
+            },
+            $userScopedAffiliations
+        );
+
+        $allowedAffiliations =  $this->getAllowedAffiliations($idpEntityId);
+        $result = array_intersect($userAffiliations, $allowedAffiliations);
+        Logger::debug(
+            self::LOGGER_PREFIX .
+            'Affiliations that make user eligible: ' . implode(",", $result)
+        );
+        return !empty($result);
+    }
+
+
+    private function getAllowedAffiliations(string $idpEntityId): array
+    {
+        $allowedAffiliations = [];
+        $eligibilities = $this->getEligibilityData();
+
+        if (array_key_exists($idpEntityId, $eligibilities)) {
+            $allowedAffiliations = $eligibilities[$idpEntityId];
+        }
+
+        return $allowedAffiliations;
+    }
+
+
+    private function getEligibilityData(): array
+    {
+        $data = 0;
+        if (file_exists(self::DATA_FILEPATH)) {
+            $file_age = strtotime(date("F d Y H:i:s.", filectime(self::DATA_FILEPATH)));
+            if ($file_age < strtotime("-" . self::LINK_REFRESH_RATE_MINS  . " minutes")) {
+                $data = $this->fetchAffiliationData();
+            }
+
+            if (empty($data) and $file_age > strtotime("-" . self::FILE_MAX_AGE_HOURS . " hours")) {
+                $data = $this->readDataFromCacheFile();
+            }
+        } else {
+            $data = $this->fetchAffiliationData();
+        }
+        return $data;
+    }
+
+    private function readDataFromCacheFile(): array
+    {
+        try {
+            $fileSize = filesize(self::DATA_FILEPATH);
+
+            $file = fopen(self::DATA_FILEPATH, "r");
+            $sourceData = fread($file, $fileSize);
+            fclose($file);
+
+            return yaml_parse($sourceData);
+        } catch (\Exception $exception) {
+            throw new \Exception(
+                self::LOGGER_PREFIX . "Cannot read local stored cache file: "
+                . self::DATA_FILEPATH
+            );
+        }
+    }
+
+    private function fetchAffiliationData(): array
+    {
+        $sourceData = file_get_contents(self::IDP_ELIGIBLE_AFFILIATIONS_LIST_LINK);
+        if (!$sourceData) {
+            Logger::debug(
+                self::LOGGER_PREFIX . "Cannot download data from " . self::IDP_ELIGIBLE_AFFILIATIONS_LIST_LINK
+            );
+            return [];
+        }
+
+        // Store to file
+        try {
+            $file = fopen(self::DATA_FILEPATH, "w");
+            fwrite($file, $sourceData);
+            fclose($file);
+        } catch (\Exception $exception) {
+            Logger::debug(
+                self::LOGGER_PREFIX . "Cannot store data to file" . self::DATA_FILEPATH
+            );
+        }
+
+        return yaml_parse($sourceData);
+    }
+}