From 55e47c942124a6038c7a2c499afb06083c6a4526 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marko=20Ivan=C4=8Di=C4=87?= <marko.ivancic@srce.hr>
Date: Thu, 6 Jul 2023 15:39:46 +0200
Subject: [PATCH] WIP

---
 src/Entities/Bases/AbstractProvider.php       | 28 +++++---
 src/Entities/Interfaces/ProviderInterface.php |  6 +-
 .../Providers/Bases/AbstractSaml2Provider.php | 69 +++++++++++++++++++
 src/Entities/Providers/Identity/Oidc.php      |  5 ++
 src/Entities/Providers/Identity/Saml2.php     | 37 ++--------
 src/Entities/Providers/Service/Oidc.php       |  5 ++
 src/Entities/Providers/Service/Saml2.php      | 40 ++---------
 .../Entities/Bases/AbstractProviderTest.php   |  5 ++
 .../Entities/Providers/Identity/Saml2Test.php |  2 +
 .../Entities/Providers/Service/Saml2Test.php  |  2 +
 tests/src/Helpers/ProviderResolverTest.php    |  2 +
 11 files changed, 119 insertions(+), 82 deletions(-)
 create mode 100644 src/Entities/Providers/Bases/AbstractSaml2Provider.php

diff --git a/src/Entities/Bases/AbstractProvider.php b/src/Entities/Bases/AbstractProvider.php
index 9e63abd..c4b66b6 100755
--- a/src/Entities/Bases/AbstractProvider.php
+++ b/src/Entities/Bases/AbstractProvider.php
@@ -18,23 +18,28 @@ abstract class AbstractProvider implements ProviderInterface
         $this->entityId = $this->resolveEntityId();
     }
 
-    protected function resolveOptionallyLocalizedString(string $key, string $locale = 'en'): ?string
-    {
-        if (!isset($this->metadata[$key])) {
+    protected function resolveOptionallyLocalizedString(
+        string $key,
+        string $locale = self::DEFAULT_LOCALE,
+        array $metadataOverride = null
+    ): ?string {
+        $metadata = $metadataOverride ?? $this->metadata;
+
+        if (!isset($metadata[$key])) {
             return null;
         }
 
         // Check for non-localized version.
-        if (is_string($this->metadata[$key])) {
-            return $this->metadata[$key];
+        if (is_string($metadata[$key])) {
+            return $metadata[$key];
         }
 
         if (
-            is_array($this->metadata[$key]) &&
-            !empty($this->metadata[$key][$locale]) &&
-            is_string($this->metadata[$key][$locale])
+            is_array($metadata[$key]) &&
+            !empty($metadata[$key][$locale]) &&
+            is_string($metadata[$key][$locale])
         ) {
-            return $this->metadata[$key][$locale];
+            return $metadata[$key][$locale];
         }
 
         return null;
@@ -50,8 +55,9 @@ abstract class AbstractProvider implements ProviderInterface
         return $this->entityId;
     }
 
-    abstract public function getName(string $locale = 'en'): ?string;
-    abstract public function getDescription(string $locale = 'en'): ?string;
+    abstract public function getName(string $locale = self::DEFAULT_LOCALE): ?string;
+    abstract public function getDescription(string $locale = self::DEFAULT_LOCALE): ?string;
     abstract protected function resolveEntityId(): string;
     abstract public function getProtocol(): AuthenticationProtocolInterface;
+    abstract protected function getProviderDescription(): string;
 }
diff --git a/src/Entities/Interfaces/ProviderInterface.php b/src/Entities/Interfaces/ProviderInterface.php
index c82a179..a5957a5 100755
--- a/src/Entities/Interfaces/ProviderInterface.php
+++ b/src/Entities/Interfaces/ProviderInterface.php
@@ -9,9 +9,11 @@ use SimpleSAML\Module\accounting\Entities\Bases\AbstractPayload;
 
 interface ProviderInterface
 {
+    public const DEFAULT_LOCALE = 'en';
+
     public function getMetadata(): array;
-    public function getName(string $locale = 'en'): ?string;
+    public function getName(string $locale = self::DEFAULT_LOCALE): ?string;
     public function getEntityId(): string;
-    public function getDescription(string $locale = 'en'): ?string;
+    public function getDescription(string $locale = self::DEFAULT_LOCALE): ?string;
     public function getProtocol(): AuthenticationProtocolInterface;
 }
diff --git a/src/Entities/Providers/Bases/AbstractSaml2Provider.php b/src/Entities/Providers/Bases/AbstractSaml2Provider.php
new file mode 100644
index 0000000..0a5fd98
--- /dev/null
+++ b/src/Entities/Providers/Bases/AbstractSaml2Provider.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace SimpleSAML\Module\accounting\Entities\Providers\Bases;
+
+use SimpleSAML\Module\accounting\Entities\Bases\AbstractProvider;
+use SimpleSAML\Module\accounting\Entities\Interfaces\AuthenticationProtocolInterface;
+use SimpleSAML\Module\accounting\Exceptions\MetadataException;
+use SimpleSAML\Module\accounting\Entities\Authentication;
+
+abstract class AbstractSaml2Provider extends AbstractProvider
+{
+    public const METADATA_KEY_ENTITY_ID = 'entityid';
+    public const METADATA_KEY_NAME = 'name';
+    public const METADATA_KEY_DESCRIPTION = 'description';
+    public const METADATA_KEY_UI_INFO = 'UIInfo';
+    public const METADATA_KEY_UI_INFO_DESCRIPTION = 'Description';
+    public const METADATA_KEY_UI_INFO_DISPLAY_NAME = 'DisplayName';
+
+    protected function getEntityInfoString(string $key, string $locale = self::DEFAULT_LOCALE): ?string
+    {
+        return $this->resolveOptionallyLocalizedString($key, $locale);
+    }
+
+    protected function getEntityUiInfoString(string $key, string $locale = self::DEFAULT_LOCALE): ?string
+    {
+        if (
+            isset($this->metadata[self::METADATA_KEY_UI_INFO]) &&
+            is_array($this->metadata[self::METADATA_KEY_UI_INFO])
+        ) {
+            return $this->resolveOptionallyLocalizedString(
+                $key,
+                $locale,
+                $this->metadata[self::METADATA_KEY_UI_INFO]
+            );
+        }
+
+        return null;
+    }
+
+    public function getName(string $locale = self::DEFAULT_LOCALE): ?string
+    {
+        return $this->getEntityInfoString(self::METADATA_KEY_NAME, $locale) ??
+            $this->getEntityUiInfoString(self::METADATA_KEY_UI_INFO_DISPLAY_NAME, $locale);
+    }
+
+    public function getDescription(string $locale = self::DEFAULT_LOCALE): ?string
+    {
+        return $this->getEntityInfoString(self::METADATA_KEY_DESCRIPTION, $locale) ??
+            $this->getEntityUiInfoString(self::METADATA_KEY_UI_INFO_DESCRIPTION, $locale);
+    }
+
+    /**
+     * @throws MetadataException
+     */
+    protected function resolveEntityId(): string
+    {
+        if (empty($entityId = $this->getEntityInfoString(self::METADATA_KEY_ENTITY_ID))) {
+            $message = sprintf('Provider metadata does not contain entity ID (%s).', $this->getProviderDescription());
+            throw new MetadataException($message);
+        }
+
+        return $entityId;
+    }
+
+    public function getProtocol(): AuthenticationProtocolInterface
+    {
+        return new Authentication\Protocol\Saml2();
+    }
+}
diff --git a/src/Entities/Providers/Identity/Oidc.php b/src/Entities/Providers/Identity/Oidc.php
index c662f78..9bc3784 100755
--- a/src/Entities/Providers/Identity/Oidc.php
+++ b/src/Entities/Providers/Identity/Oidc.php
@@ -43,4 +43,9 @@ class Oidc extends AbstractProvider implements IdentityProviderInterface
     {
         return new Authentication\Protocol\Oidc();
     }
+
+    protected function getProviderDescription(): string
+    {
+        return $this->getProtocol()->getDesignation() . ' OpenID Provider';
+    }
 }
diff --git a/src/Entities/Providers/Identity/Saml2.php b/src/Entities/Providers/Identity/Saml2.php
index 0476863..8bef30d 100755
--- a/src/Entities/Providers/Identity/Saml2.php
+++ b/src/Entities/Providers/Identity/Saml2.php
@@ -5,44 +5,15 @@ declare(strict_types=1);
 namespace SimpleSAML\Module\accounting\Entities\Providers\Identity;
 
 use SimpleSAML\Module\accounting\Entities\Authentication;
-use SimpleSAML\Module\accounting\Entities\Bases\AbstractProvider;
 use SimpleSAML\Module\accounting\Entities\Interfaces\AuthenticationProtocolInterface;
 use SimpleSAML\Module\accounting\Entities\Interfaces\IdentityProviderInterface;
+use SimpleSAML\Module\accounting\Entities\Providers\Bases\AbstractSaml2Provider;
 use SimpleSAML\Module\accounting\Exceptions\MetadataException;
 
-class Saml2 extends AbstractProvider implements IdentityProviderInterface
+class Saml2 extends AbstractSaml2Provider implements IdentityProviderInterface
 {
-    public const METADATA_KEY_NAME = 'name';
-    public const METADATA_KEY_ENTITY_ID = 'entityid';
-    public const METADATA_KEY_DESCRIPTION = 'description';
-
-    public function getName(string $locale = 'en'): ?string
-    {
-        return $this->resolveOptionallyLocalizedString(self::METADATA_KEY_NAME, $locale);
-    }
-
-    public function getDescription(string $locale = 'en'): ?string
-    {
-        return $this->resolveOptionallyLocalizedString(self::METADATA_KEY_DESCRIPTION, $locale);
-    }
-
-    /**
-     * @throws MetadataException
-     */
-    protected function resolveEntityId(): string
-    {
-        if (
-            !empty($this->metadata[self::METADATA_KEY_ENTITY_ID]) &&
-            is_string($this->metadata[self::METADATA_KEY_ENTITY_ID])
-        ) {
-            return $this->metadata[self::METADATA_KEY_ENTITY_ID];
-        }
-
-        throw new MetadataException('Identity provider metadata does not contain entity ID.');
-    }
-
-    public function getProtocol(): AuthenticationProtocolInterface
+    protected function getProviderDescription(): string
     {
-        return new Authentication\Protocol\Saml2();
+        return $this->getProtocol()->getDesignation() . ' Identity Provider';
     }
 }
diff --git a/src/Entities/Providers/Service/Oidc.php b/src/Entities/Providers/Service/Oidc.php
index 5f5ff82..c683718 100755
--- a/src/Entities/Providers/Service/Oidc.php
+++ b/src/Entities/Providers/Service/Oidc.php
@@ -45,4 +45,9 @@ class Oidc extends AbstractProvider implements ServiceProviderInterface
     {
         return new Authentication\Protocol\Oidc();
     }
+
+    protected function getProviderDescription(): string
+    {
+        return $this->getProtocol()->getDesignation() . ' Relying Party';
+    }
 }
diff --git a/src/Entities/Providers/Service/Saml2.php b/src/Entities/Providers/Service/Saml2.php
index d0dfd4a..deeb307 100755
--- a/src/Entities/Providers/Service/Saml2.php
+++ b/src/Entities/Providers/Service/Saml2.php
@@ -4,45 +4,13 @@ declare(strict_types=1);
 
 namespace SimpleSAML\Module\accounting\Entities\Providers\Service;
 
-use SimpleSAML\Module\accounting\Entities\Authentication;
-use SimpleSAML\Module\accounting\Entities\Bases\AbstractProvider;
-use SimpleSAML\Module\accounting\Entities\Interfaces\AuthenticationProtocolInterface;
 use SimpleSAML\Module\accounting\Entities\Interfaces\ServiceProviderInterface;
-use SimpleSAML\Module\accounting\Exceptions\MetadataException;
+use SimpleSAML\Module\accounting\Entities\Providers\Bases\AbstractSaml2Provider;
 
-class Saml2 extends AbstractProvider implements ServiceProviderInterface
+class Saml2 extends AbstractSaml2Provider implements ServiceProviderInterface
 {
-    public const METADATA_KEY_ENTITY_ID = 'entityid';
-    public const METADATA_KEY_NAME = 'name';
-    public const METADATA_KEY_DESCRIPTION = 'description';
-
-    public function getName(string $locale = 'en'): ?string
-    {
-        return $this->resolveOptionallyLocalizedString(self::METADATA_KEY_NAME, $locale);
-    }
-
-    public function getDescription(string $locale = 'en'): ?string
-    {
-        return $this->resolveOptionallyLocalizedString(self::METADATA_KEY_DESCRIPTION, $locale);
-    }
-
-    /**
-     * @throws MetadataException
-     */
-    protected function resolveEntityId(): string
-    {
-        if (
-            !empty($this->metadata[self::METADATA_KEY_ENTITY_ID]) &&
-            is_string($this->metadata[self::METADATA_KEY_ENTITY_ID])
-        ) {
-            return $this->metadata[self::METADATA_KEY_ENTITY_ID];
-        }
-
-        throw new MetadataException('Service provider metadata does not contain entity ID.');
-    }
-
-    public function getProtocol(): AuthenticationProtocolInterface
+    protected function getProviderDescription(): string
     {
-        return new Authentication\Protocol\Saml2();
+        return $this->getProtocol()->getDesignation() . ' Service Provider';
     }
 }
diff --git a/tests/src/Entities/Bases/AbstractProviderTest.php b/tests/src/Entities/Bases/AbstractProviderTest.php
index e8d8a27..e858379 100755
--- a/tests/src/Entities/Bases/AbstractProviderTest.php
+++ b/tests/src/Entities/Bases/AbstractProviderTest.php
@@ -112,6 +112,11 @@ class AbstractProviderTest extends TestCase
                     }
                 };
             }
+
+            protected function getProviderDescription(): string
+            {
+                return 'provider description';
+            }
         };
     }
 }
diff --git a/tests/src/Entities/Providers/Identity/Saml2Test.php b/tests/src/Entities/Providers/Identity/Saml2Test.php
index 7c99595..5c4db09 100755
--- a/tests/src/Entities/Providers/Identity/Saml2Test.php
+++ b/tests/src/Entities/Providers/Identity/Saml2Test.php
@@ -12,6 +12,8 @@ use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
 /**
  * @covers \SimpleSAML\Module\accounting\Entities\Providers\Identity\Saml2
  * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractProvider
+ * @uses \SimpleSAML\Module\accounting\Entities\Providers\Bases\AbstractSaml2Provider
+ * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2
  */
 class Saml2Test extends TestCase
 {
diff --git a/tests/src/Entities/Providers/Service/Saml2Test.php b/tests/src/Entities/Providers/Service/Saml2Test.php
index c4475d7..225c92a 100755
--- a/tests/src/Entities/Providers/Service/Saml2Test.php
+++ b/tests/src/Entities/Providers/Service/Saml2Test.php
@@ -9,6 +9,8 @@ use SimpleSAML\Module\accounting\Exceptions\MetadataException;
 /**
  * @covers \SimpleSAML\Module\accounting\Entities\Providers\Service\Saml2
  * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractProvider
+ * @uses \SimpleSAML\Module\accounting\Entities\Providers\Bases\AbstractSaml2Provider
+ * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2
  */
 class Saml2Test extends TestCase
 {
diff --git a/tests/src/Helpers/ProviderResolverTest.php b/tests/src/Helpers/ProviderResolverTest.php
index 3661701..1e7f3fe 100755
--- a/tests/src/Helpers/ProviderResolverTest.php
+++ b/tests/src/Helpers/ProviderResolverTest.php
@@ -21,6 +21,8 @@ use SimpleSAML\Test\Module\accounting\Constants\StateArrays;
  * @uses \SimpleSAML\Module\accounting\Entities\Providers\Identity\Oidc
  * @uses \SimpleSAML\Module\accounting\Entities\Providers\Service\Saml2
  * @uses \SimpleSAML\Module\accounting\Entities\Providers\Service\Oidc
+ * @uses \SimpleSAML\Module\accounting\Entities\Providers\Bases\AbstractSaml2Provider
+ * @uses \SimpleSAML\Module\accounting\Entities\Authentication\Protocol\Saml2
  */
 class ProviderResolverTest extends TestCase
 {
-- 
GitLab