<?PHP
#
#   FILE:  CreateUser.php
#
#   Part of the Collection Workflow Integration System (CWIS)
#   Copyright 2011 Edward Almasy and Internet Scout
#   http://scout.wisc.edu
#

/*
* Add support for logging in using OpenId credentials.
*/

class OpenId extends Plugin
{
    /**
    * Register the OpenId plugin.
    * @see Plugin::Register
    */
    function Register()
    {
        $this->Name = "OpenID Login";
        $this->Version = "1.0.3";
        $this->Description = "Provides users with the option to log in with an "
                ."<a href=\"http://openid.net\" target=\"_blank\">OpenID</a>, "
                ."allowing them to use the same login across multiple websites.";
        $this->Author = "Internet Scout";
        $this->Url = "http://scout.wisc.edu/cwis/";
        $this->Email = "scout@scout.wisc.edu";
        $this->Requires = array(
            "CWISCore" => "1.9.0"
            );

        $this->CfgSetup["EnableWhitelist"] = array(
            "Type" => "Flag",
            "Label" => "Enable whitelist",
            "Help" => "If enabled, OpenID logins will only be allowed from "
            ."whitelisted providers",
            "OnLabel" => "Yes",
            "OffLabel"=> "No",
            "Default" => FALSE,
            );

        $this->CfgSetup["AdditionalWhitelist"] = array(
            "Type" => "Paragraph",
            "Label" => "Additional OpenId Providers",
            "Help" => "This extends the built-in whitelist of OpenID providers "
            ."from whom logins are accepted.<br>"
            ."Regular expressions (PCRE format) or domains should be entered, "
            ."one per line, to match the Provider URLs you whish to accept.<br>"
            ."OpenIDs from Google, AOL, and Yahoo are always accepted.",
            "Columns" => 80,
            );
    }

    /**
    * Install the OpenId plugin.
    * @see Plugin::Install
    */
    function Install()
    {
        $DB = new Database();

        $DB->Query("CREATE TABLE OpenId_UserMap ("
                   ."DisplayId TEXT, "
                   ."UserName TEXT)"
            );

    }

    /**
    * Hook the OpenId plugin into the event system.
    * @see Plugin::HookEvents
    */
    function HookEvents()
    {
        return array(
            "EVENT_APPEND_HTML_TO_FORM" => "ModifyLoginForm",
            "EVENT_PHP_FILE_LOAD" => "InspectFormAndMaybeRedirect");
    }

    /**
    * Look for OpenID login attempts and redirect them into the OpenID
    * plugin rather than through the normal login pages.
    * @param PageName The page being loaded.
    * See EVENT_PHP_FILE_LOAD  in @ref uievents "User Interface Events" for
    * more details.
    */
    function InspectFormAndMaybeRedirect($PageName)
    {
        $Destination = array("PageName" => $PageName);
        $OidDisplayed = (bool) $this->GetPostValue("F_OidDisplayed");
        $Provider = $this->GetPostValue("F_OpenIdProvider");
        $Name = $this->GetPostValue("F_OpenId");
        $Username = $this->GetPostValue("F_UserName");
        $Password = $this->GetPostValue("F_Password");
        $Skip = isset($_POST["SkipOpenId"]) || $Username
            || $Password || !$OidDisplayed;

        if ($Provider && array_key_exists($Provider, $this->ProviderUrls))
        {
            $ProviderUrl = $this->ProviderUrls[$Provider];

            # AOL is currently the only provider supporting user names
            if ($Provider == "aol")
            {
                $Name = $ProviderUrl . $Name;
                $_POST["F_OpenId"] = $Name;
            }

            else
            {
                $Name = $ProviderUrl;
                $_POST["F_OpenId"] = $Name;
            }
        }

        if ($PageName == "UserLogin" && !$Skip && $Name)
        {
            if ($this->ConfigSetting("EnableWhitelist"))
            {
                $WhitelistMatch = FALSE;

                $MyWhitelist = $this->Whitelist;

                foreach (explode("\n", $this->ConfigSetting("AdditionalWhitelist")) as $Line)
                {
                    $TrimLine = trim($Line);
                    if (strlen($TrimLine)>0)
                    {
                        # If this line appears to be a PCRE Regex, then just accept it as-is
                        # Otherwise, add PCRE wrappings to it.
                        if ( $TrimLine[0]=="/" &&
                             $TrimLine[strlen($TrimLine)-1]=="/")
                        {
                            $MyWhitelist []= $TrimLine;
                        }
                        else
                        {
                            $MyWhitelist []= "/^(https?:\\/\\/)?([A-Za-z0-9-]+\\.)?".
                                preg_quote($TrimLine)."/";
                        }
                    }
                }

                foreach ($MyWhitelist as $WhitelistPattern)
                {
                    if (preg_match($WhitelistPattern, $Name) == 1)
                    {
                        $WhitelistMatch = TRUE;
                        break;
                    }
                }
            }
            else
            {
                $WhitelistMatch = TRUE;
            }

            if ($WhitelistMatch)
            {
                $Destination["PageName"] = "P_OpenId_TryAuth";
                if (isset($_SERVER["HTTP_REFERER"]))
                    $_SESSION["ReturnToPage"] = $_SERVER["HTTP_REFERER"] ;
            }
        }

        return $Destination;
    }

    /**
    * Add the OpenID login box to the sidebar.  See
    * EVENT_APPEND_HTML_TO_FORM in @ref uievents "User Interface
    * Events" for more details.
    */
    function ModifyLoginForm($PageName, $FormName, $Labels, $InputElements, $Notes)
    {
        global $AF;

        $Result = array(
            "PageName" => $PageName,
            "FormName" => $FormName,
            "Labels" => $Labels,
            "InputElements" => $InputElements,
            "Notes" => $Notes);

        # modify the login form only
        if ($FormName != "LoginForm")
        {
            return $Result;
        }

        if (1 > strlen($PageName))
        {
            # header
            $Result["Labels"][] = '
                <p class="oid-header"><b>Log In Using OpenID</b>:</p>';
            $Result["InputElements"][] = "";
            $Result["Notes"][] = "";

            # hidden flag field
            $Result["Labels"][] = "";
            $Result["InputElements"][] = '
                <input type="hidden" name="F_OidDisplayed" value="1" />';
            $Result["Notes"][] = "";

            # provider field
            $Result["Labels"][] = '
                <label class="oid-provider" for="F_OpenIdProvider">Use:</label>';
            $Result["InputElements"][] ='
                <select class="oid-provider" id="F_OpenIdProvider" name="F_OpenIdProvider">
                    <option value="">OpenID</option>
                    <option value="google">Google</option>
                    <option value="yahoo">Yahoo!</option>
                    <option value="aol">AOL</option>
                </select>';
            $Result["Notes"][] = "";

            # OpenID username field, submit button, styles, and javascript
            $Result["Labels"][] = '
                <label class="oid-name" for="F_OpenId">Name:</label>';
            $Result["InputElements"][] = '
                <input class="oid-name" type="text" id="F_OpenId" name="F_OpenId" value="" />
                <input class="oid-submit" type="image"
                    src="'.$AF->GUIFile("go.gif").'" alt="GO" />
                <style type="text/css">
                    p.oid-header {}
                    label.oid-provider { display: block; }
                    select.oid-provider { width: 108px; margin-top: 1px; }
                    div.oid-namegroup { display: block; }
                    label.oid-name { display: block; margin-top: 2px; }
                    input.oid-name { position: relative; bottom: 5px; width: 104px; }
                    input.oid-submit { position: relative; top: 1px; left: 7px; }
                </style>
                <script type="text/javascript" src="include/jquery.cookie.js"></script>
                <script type="text/javascript">
                  (function(){
                    var $providers, cookie;
                    if ($ && $.cookie) {
                      $providers = $("select.oid-provider");
                      cookie = $.cookie("oid-provider");

                      $providers.change(function(){
                        var $provider = $(this),
                            $option = $("option:selected", $provider),
                            $name = $("input.oid-name"),
                            label = $option.html();

                        $name.val("");
                        $.cookie("oid-provider", label, {
                            "expires": 365
                        });
                      });

                      if (cookie) {
                        $("option:selected", $providers).removeAttr("selected");
                        $("option:contains(\'"+cookie+"\')",
                            $providers).attr("selected", "selected");
                        $providers.change();
                      } else {
                        $("option:contains(\'Google\')",
                            $providers).attr("selected", "selected");
                      }
                    }
                  }())
                </script>';
            $Result["Notes"][] = trim(preg_replace('/\s+/', " ", "
                To login with an OpenId, enter your OpenID URL below.
                Login with a Google Profile by entering
                'google.com/profiles/YOUR_GOOGLE_PROFILE'. Login with yahoo by
                entering 'yahoo.com'"));
        }

        else if ($PageName == "LoginError")
        {
            if (isset($_SESSION['OidLoginError']))
            {
                $Result["Labels"][] = $_SESSION['OidLoginError'];
                $Result["InputElements"][] = "&nbsp;";
                $Result["Notes"][] = "";

                unset($_SESSION['OidLoginError']);
            }
        }

        return $Result;
    }

    /**
     * Get a value from the POST array.
     * @param $Key POST array key
     * @return the value or NULL if not set or is an empty string
     */
    private function GetPostValue($Key)
    {
        if (!isset($_POST[$Key]) || 1 > strlen($_POST[$Key]))
        {
            return NULL;
        }

        return $_POST[$Key];
    }

    /**
     * @var $Providers OpenID provider URLs
     */
    private $ProviderUrls = array(
        "aol" => "https://openid.aol.com/",
        "google" => "https://www.google.com/accounts/o8/id",
        "yahoo" => "https://www.yahoo.com/");

    /**
     * @var $Whitelist Default whitelist including the OpenId Providers from our menu
     */
    private $Whitelist = array(
        "/^(https?:\/\/)?openid\.aol\.com/",
        "/^(https?:\/\/)?www\.google\.com\/accounts\/o8\/id/",
        "/^(https?:\/\/)?www\.yahoo\.com/",
        );
}
