]> git.mxchange.org Git - friendica-addons.git/blobdiff - saml/saml.php
Bluesky: Tags are now supported
[friendica-addons.git] / saml / saml.php
index 5c50017d0b652b78358734610453d91126803827..56dd0f5d23f9ea2318197f8d818e90c773258dd4 100755 (executable)
@@ -2,41 +2,44 @@
 /*
  * Name: SAML SSO and SLO
  * Description: replace login and registration with a SAML identity provider.
- * Version: 0.0
+ * Version: 1.0
  * Author: Ryan <https://friendica.verya.pe/profile/ryan>
  */
+
 use Friendica\Content\Text\BBCode;
 use Friendica\Core\Hook;
 use Friendica\Core\Logger;
 use Friendica\Core\Renderer;
-use Friendica\Core\Session;
 use Friendica\Database\DBA;
 use Friendica\DI;
 use Friendica\Model\User;
-use Friendica\Util\Strings;
+use OneLogin\Saml2\Utils;
 
 require_once(__DIR__ . '/vendor/autoload.php');
 
-define("PW_LEN", 32); // number of characters to use for random passwords
+define('PW_LEN', 32); // number of characters to use for random passwords
 
-function saml_module($a)
-{
-}
+function saml_module() {}
 
-function saml_init($a)
+function saml_init()
 {
-       if ($a->argc < 2) {
+       if (DI::args()->getArgc() < 2) {
+               return;
+       }
+
+       if (!saml_is_configured()) {
+               echo 'Please configure the SAML add-on via the admin interface.';
                return;
        }
 
-       switch ($a->argv[1]) {
-               case "metadata.xml":
+       switch (DI::args()->get(1)) {
+               case 'metadata.xml':
                        saml_metadata();
                        break;
-               case "sso":
-                       saml_sso_reply($a);
+               case 'sso':
+                       saml_sso_reply();
                        break;
-               case "slo":
+               case 'slo':
                        saml_slo_reply();
                        break;
        }
@@ -72,15 +75,15 @@ function saml_install()
        Hook::register('footer', __FILE__, 'saml_footer');
 }
 
-function saml_head(&$a, &$b)
+function saml_head(string &$body)
 {
        DI::page()->registerStylesheet(__DIR__ . '/saml.css');
 }
 
-function saml_footer(&$a, &$b)
+function saml_footer(string &$body)
 {
-       $fragment = addslashes(BBCode::convert(DI::config()->get('saml', 'settings_statement')));
-       $b .= <<<EOL
+       $fragment = addslashes(BBCode::convertForUriId(User::getSystemUriId(), DI::config()->get('saml', 'settings_statement')));
+       $body .= <<<EOL
 <script>
 var target=$("#settings-nickname-desc");
 if (target.length) { target.append("<p>$fragment</p>"); }
@@ -101,43 +104,44 @@ function saml_is_configured()
                DI::config()->get('saml', 'idp_cert');
 }
 
-function saml_sso_initiate(&$a, &$b)
+function saml_sso_initiate(string &$body)
 {
        if (!saml_is_configured()) {
+               Logger::warning('SAML SSO tried to trigger, but the SAML addon is not configured yet!');
                return;
        }
 
        $auth = new \OneLogin\Saml2\Auth(saml_settings());
-       $ssoBuiltUrl = $auth->login(null, array(), false, false, true);
-       $_SESSION['AuthNRequestID'] = $auth->getLastRequestID();
+       $ssoBuiltUrl = $auth->login(null, [], false, false, true);
+       DI::session()->set('AuthNRequestID', $auth->getLastRequestID());
        header('Pragma: no-cache');
        header('Cache-Control: no-cache, must-revalidate');
        header('Location: ' . $ssoBuiltUrl);
        exit();
 }
 
-function saml_sso_reply($a)
+function saml_sso_reply()
 {
        $auth = new \OneLogin\Saml2\Auth(saml_settings());
        $requestID = null;
 
-       if (isset($_SESSION) && isset($_SESSION['AuthNRequestID'])) {
-               $requestID = $_SESSION['AuthNRequestID'];
+       if (DI::session()->exists('AuthNRequestID')) {
+               $requestID = DI::session()->get('AuthNRequestID');
        }
 
        $auth->processResponse($requestID);
-       unset($_SESSION['AuthNRequestID']);
+       DI::session()->remove('AuthNRequestID');
 
        $errors = $auth->getErrors();
 
        if (!empty($errors)) {
-               echo "Errors encountered.";
+               echo 'Errors encountered.';
                Logger::error(implode(', ', $errors));
                exit();
        }
 
        if (!$auth->isAuthenticated()) {
-               echo "Not authenticated";
+               echo 'Not authenticated';
                exit();
        }
 
@@ -157,21 +161,25 @@ function saml_sso_reply($a)
        }
 
        if (!empty($user['uid'])) {
-               DI::auth()->setForUser($a, $user);
+               DI::auth()->setForUser(DI::app(), $user);
        }
 
-       if (isset($_POST['RelayState'])
-               && \OneLogin\Saml2\Utils::getSelfURL() != $_POST['RelayState']) {
+       if (isset($_POST['RelayState']) && Utils::getSelfURL() != $_POST['RelayState']) {
                $auth->redirectTo($_POST['RelayState']);
        }
 }
 
-function saml_slo_initiate(&$a, &$b)
+function saml_slo_initiate()
 {
+       if (!saml_is_configured()) {
+               Logger::warning('SAML SLO tried to trigger, but the SAML addon is not configured yet!');
+               return;
+       }
+
        $auth = new \OneLogin\Saml2\Auth(saml_settings());
 
        $sloBuiltUrl = $auth->logout();
-       $_SESSION['LogoutRequestID'] = $auth->getLastRequestID();
+       DI::session()->set('LogoutRequestID', $auth->getLastRequestID());
        header('Pragma: no-cache');
        header('Cache-Control: no-cache, must-revalidate');
        header('Location: ' . $sloBuiltUrl);
@@ -182,8 +190,8 @@ function saml_slo_reply()
 {
        $auth = new \OneLogin\Saml2\Auth(saml_settings());
 
-       if (isset($_SESSION) && isset($_SESSION['LogoutRequestID'])) {
-               $requestID = $_SESSION['LogoutRequestID'];
+       if (DI::session()->exists('LogoutRequestID')) {
+               $requestID = DI::session()->get('LogoutRequestID');
        } else {
                $requestID = null;
        }
@@ -207,12 +215,12 @@ function saml_input($key, $label, $description)
                        $label,
                        DI::config()->get('saml', $key),
                        $description,
-               true, // all the fields are required
+                       true, // all the fields are required
                ]
        ];
 }
 
-function saml_addon_admin(&$a, &$o)
+function saml_addon_admin(string &$o)
 {
        $form =
                saml_input(
@@ -264,27 +272,25 @@ function saml_addon_admin(&$a, &$o)
                [
                        '$submit'  => DI::l10n()->t('Save Settings'),
                ];
-       $t = Renderer::getMarkupTemplate("admin.tpl", "addon/saml/");
+       $t = Renderer::getMarkupTemplate('admin.tpl', 'addon/saml/');
        $o = Renderer::replaceMacros($t, $form);
 }
 
-function saml_addon_admin_post(&$a)
+function saml_addon_admin_post()
 {
-       $safeset = function ($key) {
-               $val = (!empty($_POST[$key]) ? Strings::escapeTags(trim($_POST[$key])) : '');
+       $set = function ($key) {
+               $val = (!empty($_POST[$key]) ? trim($_POST[$key]) : '');
                DI::config()->set('saml', $key, $val);
        };
-       $safeset('idp_id');
-       $safeset('client_id');
-       $safeset('sso_url');
-       $safeset('slo_request_url');
-       $safeset('slo_response_url');
-       $safeset('sp_key');
-       $safeset('sp_cert');
-       $safeset('idp_cert');
-
-       // Not using safeset here since settings_statement is *meant* to include HTML tags.
-       DI::config()->set('saml', 'settings_statement', $_POST['settings_statement']);
+       $set('idp_id');
+       $set('client_id');
+       $set('sso_url');
+       $set('slo_request_url');
+       $set('slo_response_url');
+       $set('sp_key');
+       $set('sp_cert');
+       $set('idp_cert');
+       $set('settings_statement');
 }
 
 function saml_create_user($username, $email, $name)
@@ -307,7 +313,7 @@ function saml_create_user($username, $email, $name)
                        'nickname' => $username,
                        'email' => $email,
                        'password' => base64_encode($bytes), // should be at least PW_LEN long
-               'verified' => true
+                       'verified' => true
                ]);
 
                return $user;
@@ -329,7 +335,8 @@ function saml_create_user($username, $email, $name)
 
 function saml_settings()
 {
-       return array(
+       return [
+
                // If 'strict' is True, then the PHP Toolkit will reject unsigned
                // or unencrypted messages if it expects them to be signed or encrypted.
                // Also it will reject the messages if the SAML standard is not strictly
@@ -344,101 +351,120 @@ function saml_settings()
                // the BaseURL of the view that process the SAML Message.
                // Ex http://sp.example.com/
                //      http://example.com/sp/
-               'baseurl' => DI::baseUrl() . "/saml",
+               'baseurl' => DI::baseUrl() . '/saml',
 
                // Service Provider Data that we are deploying.
-               'sp' => array(
+               'sp' => [
+
                        // Identifier of the SP entity  (must be a URI)
                        'entityId' => DI::config()->get('saml', 'client_id'),
+
                        // Specifies info about where and how the <AuthnResponse> message MUST be
                        // returned to the requester, in this case our SP.
-                       'assertionConsumerService' => array(
+                       'assertionConsumerService' => [
+
                                // URL Location where the <Response> from the IdP will be returned
-                               'url' => DI::baseUrl() . "/saml/sso",
+                               'url' => DI::baseUrl() . '/saml/sso',
+
                                // SAML protocol binding to be used when returning the <Response>
                                // message. OneLogin Toolkit supports this endpoint for the
                                // HTTP-POST binding only.
                                'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
-                       ),
+                       ],
+
                        // If you need to specify requested attributes, set a
                        // attributeConsumingService. nameFormat, attributeValue and
                        // friendlyName can be omitted
-                       "attributeConsumingService"=> array(
-                               "serviceName" => "Friendica SAML SSO and SLO Addon",
-                               "serviceDescription" => "SLO and SSO support for Friendica",
-                               "requestedAttributes" => array(
-                                       array(
-                                       "uid" => "",
-                                       "isRequired" => false,
-                                       )
-                               )
-                       ),
+                       'attributeConsumingService'=> [
+                               'serviceName' => 'Friendica SAML SSO and SLO Addon',
+                               'serviceDescription' => 'SLO and SSO support for Friendica',
+                               'requestedAttributes' => [
+                                       [
+                                               'uid' => '',
+                                               'isRequired' => false,
+                                       ]
+                               ]
+                       ],
+
                        // Specifies info about where and how the <Logout Response> message MUST be
                        // returned to the requester, in this case our SP.
-                       'singleLogoutService' => array(
+                       'singleLogoutService' => [
+
                                // URL Location where the <Response> from the IdP will be returned
-                               'url' => DI::baseUrl() . "/saml/slo",
+                               'url' => DI::baseUrl() . '/saml/slo',
+
                                // SAML protocol binding to be used when returning the <Response>
                                // message. OneLogin Toolkit supports the HTTP-Redirect binding
                                // only for this endpoint.
                                'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
-                       ),
+                       ],
+
                        // Specifies the constraints on the name identifier to be used to
                        // represent the requested subject.
                        // Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported.
                        'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
+
                        // Usually x509cert and privateKey of the SP are provided by files placed at
                        // the certs folder. But we can also provide them with the following parameters
                        'x509cert' => DI::config()->get('saml', 'sp_cert'),
                        'privateKey' => DI::config()->get('saml', 'sp_key'),
-               ),
+               ],
 
                // Identity Provider Data that we want connected with our SP.
-               'idp' => array(
+               'idp' => [
+
                        // Identifier of the IdP entity  (must be a URI)
                        'entityId' => DI::config()->get('saml', 'idp_id'),
+
                        // SSO endpoint info of the IdP. (Authentication Request protocol)
-                       'singleSignOnService' => array(
+                       'singleSignOnService' => [
+
                                // URL Target of the IdP where the Authentication Request Message
                                // will be sent.
                                'url' => DI::config()->get('saml', 'sso_url'),
+
                                // SAML protocol binding to be used when returning the <Response>
                                // message. OneLogin Toolkit supports the HTTP-Redirect binding
                                // only for this endpoint.
                                'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
-                       ),
+                       ],
+
                        // SLO endpoint info of the IdP.
-                       'singleLogoutService' => array(
+                       'singleLogoutService' => [
+
                                // URL Location of the IdP where SLO Request will be sent.
                                'url' => DI::config()->get('saml', 'slo_request_url'),
+
                                // URL location of the IdP where SLO Response will be sent (ResponseLocation)
                                // if not set, url for the SLO Request will be used
                                'responseUrl' => DI::config()->get('saml', 'slo_response_url'),
+
                                // SAML protocol binding to be used when returning the <Response>
                                // message. OneLogin Toolkit supports the HTTP-Redirect binding
                                // only for this endpoint.
                                'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
-                  ),
-                  // Public x509 certificate of the IdP
-                  'x509cert' => DI::config()->get('saml', 'idp_cert'),
-          ),
-          'security' => array (
-                  'wantXMLValidation' => false,
-
-          // Indicates whether the <samlp:AuthnRequest> messages sent by this SP
-          // will be signed.  [Metadata of the SP will offer this info]
-          'authnRequestsSigned' => true,
-
-          // Indicates whether the <samlp:logoutRequest> messages sent by this SP
-          // will be signed.
-          'logoutRequestSigned' => true,
-
-          // Indicates whether the <samlp:logoutResponse> messages sent by this SP
-          // will be signed.
-          'logoutResponseSigned' => true,
-
-          /* Sign the Metadata */
-          'signMetadata' => true,
-          )
-       );
+                       ],
+
+                       // Public x509 certificate of the IdP
+                       'x509cert' => DI::config()->get('saml', 'idp_cert'),
+               ],
+               'security' => [
+                       'wantXMLValidation' => false,
+
+                       // Indicates whether the <samlp:AuthnRequest> messages sent by this SP
+                       // will be signed.  [Metadata of the SP will offer this info]
+                       'authnRequestsSigned' => true,
+
+                       // Indicates whether the <samlp:logoutRequest> messages sent by this SP
+                       // will be signed.
+                       'logoutRequestSigned' => true,
+
+                       // Indicates whether the <samlp:logoutResponse> messages sent by this SP
+                       // will be signed.
+                       'logoutResponseSigned' => true,
+
+                       // Sign the Metadata
+                       'signMetadata' => true,
+               ]
+       ];
 }