Вход Регистрация
Файл: admin/sources/loginauth/live/lib/windowslivelogin.php
Строк: 2044
<?php

  
/* TODO: Comments are out of date and incomplete. */

/**
 * FILE:        windowslivelogin.php
 *
 * DESCRIPTION: Sample implementation of Web Authentication and Delegated 
 *              Authentication protocol in PHP. Also includes trusted 
 *              sign-in and application verification sample 
 *              implementations.
 *
 * VERSION:     1.1
 *
 * Copyright (c) 2008 Microsoft Corporation.  All Rights Reserved.
 */

/**
 * Holds the user information after a successful sign-in.
 */
class WLL_User
{

    
/**
    * Initialize the User with time stamp, userid, flags, context and token.
    */
    
public function __construct($timestamp$id$flags$context$token)
    {
        
self::setTimestamp($timestamp);
        
self::setId($id);
        
self::setFlags($flags);
        
self::setContext($context);
        
self::setToken($token);
    }

    private 
$_timestamp;
    
    
/**
     * Returns the Unix timestamp as obtained from the SSO token.
     */
    
public function getTimestamp()
    {
        return 
$this->_timestamp;
    }

    
/**
     * Sets the Unix timestamp.
     */
    
private function setTimestamp($timestamp)
    {
        if (!
$timestamp) {
            throw new 
Exception('Error: WLL_User: Null timestamp.');
        }

        if (!
preg_match('/^d+$/'$timestamp) || ($timestamp <= 0)) {
            throw new 
Exception('Error: WLL_User: Invalid timestamp: ' 
                                
$timestamp);
        }
        
        
$this->_timestamp $timestamp;
    }

    private 
$_id;

    
/**
     * Returns the pairwise unique ID for the user.
     */
    
public function getId()
    {
        return 
$this->_id;
    }

    
/**
     * Sets the pairwise unique ID for the user.
     */
    
private function setId($id)
    {
        if (!
$id) {
            throw new 
Exception('Error: WLL_User: Null id.');
        }

        if (!
preg_match('/^w+$/'$id)) {
            throw new 
Exception('Error: WLL_User: Invalid id: ' $id);
        }
        
        
$this->_id $id;
    }

    private 
$_usePersistentCookie;

    
/**
     * Indicates whether the application is expected to store the
     * user token in a session or persistent cookie.
     */
    
public function usePersistentCookie() 
    {
        return 
$this->_usePersistentCookie;
    }

    
/**
     * Sets the usePersistentCookie flag for the user.
     */
    
private function setFlags($flags)
    {
        
$this->_usePersistentCookie false;
        if (
preg_match('/^d+$/'$flags)) {
            
$this->_usePersistentCookie = (($flags 2) == 1);
        }
    }

    private 
$_context;
    
    
/** 
     * Returns the application context that was originally passed
     * to the sign-in request, if any.
     */
    
public function getContext()
    {
        return 
$this->_context;
    }

    
/**
     * Sets the the Application context.
     */
    
private function setContext($context)
    {
        
$this->_context $context;
    }

    private 
$_token;

    
/**
     * Returns the encrypted Web Authentication token containing 
     * the UID. This can be cached in a cookie and the UID can be
     * retrieved by calling the ProcessToken method.
     */
    
public function getToken()
    {
        return 
$this->_token;
    }

    
/**
     * Sets the the User token.
     */
    
private function setToken($token)
    {
        
$this->_token $token;
    }
}

/**
 * Holds the Consent Token object corresponding to consent granted. 
 */
class WLL_ConsentToken
{
    
/**
     * Indicates whether the delegation token is set and has not expired.
     */
    
public function isValid()
    {
        if (!
self::getDelegationToken()) {
            return 
false;
        }
        
        
$now time();
        return ((
$now-300) < self::getExpiry());
    }

    
/**
     * Refreshes the current token and replace it. If operation succeeds 
     * true is returned to signify success.
     */
    
public function refresh()
    {
        
$wll $this->_wll;
        
$ct $wll->refreshConsentToken($this);
        if (!
$ct) {
            return 
false;
        }
        
self::copy($ct);
        return 
true;
    }

    private 
$_wll;

    
/**
     * Initialize the ConsentToken module with the WindowsLiveLogin, 
     * delegation token, refresh token, session key, expiry, offers, 
     * location ID, context, decoded token, and raw token.
     */    
    
public function __construct($wll$delegationtoken$refreshtoken
                                
$sessionkey$expiry$offers$locationID$context
                                
$decodedtoken$token)
    {
        
$this->_wll $wll;
        
self::setDelegationToken($delegationtoken);
        
self::setRefreshToken($refreshtoken);
        
self::setSessionKey($sessionkey);
        
self::setExpiry($expiry);
        
self::setOffers($offers);
        
self::setLocationID($locationID);
        
self::setContext($context);
        
self::setDecodedToken($decodedtoken);
        
self::setToken($token);
    }

    private 
$_delegationtoken;

    
/**
     * Gets the Delegation token.
     */
    
public function getDelegationToken()
    {
        return 
$this->_delegationtoken;
    }

    
/**
     * Sets the Delegation token.
     */
    
private function setDelegationToken($delegationtoken)
    {
        if (!
$delegationtoken) {
            throw new 
Exception('Error: WLL_ConsentToken: Null delegation token.');
        }
        
$this->_delegationtoken $delegationtoken;
    }

    private 
$_refreshtoken;

    
/**
     * Gets the refresh token.
     */
    
public function getRefreshToken()
    {
        return 
$this->_refreshtoken;
    }

    
/**
     * Sets the refresh token.
     */
    
private function setRefreshToken($refreshtoken)
    {
        
$this->_refreshtoken $refreshtoken;
    }

    private 
$_sessionkey;

    
/**
     * Gets the session key.
     */
    
public function getSessionKey()
    {
        return 
$this->_sessionkey;
    }

    
/**
     * Sets the session key.
     */
    
private function setSessionKey($sessionkey)
    {
        if (!
$sessionkey) {
            throw new 
Exception('Error: WLL_ConsentToken: Null session key.');
        }
        
$this->_sessionkey base64_decode(urldecode($sessionkey));
    }

    private 
$_expiry;
    
    
/**
     * Gets the expiry time of delegation token.
     */
    
public function getExpiry()
    {
        return 
$this->_expiry;
    }

    
/**
     * Sets the expiry time of delegation token.
     */
    
private function setExpiry($expiry)
    {
        if (!
$expiry) {
            throw new 
Exception('Error: WLL_ConsentToken: Null expiry time.');
        }

        if (!
preg_match('/^d+$/'$expiry) || ($expiry <= 0)) {
            throw new 
Exception('Error: WLL_ConsentToken: Invalid expiry time: '
                                
$expiry);
        }
        
$this->_expiry $expiry;
    }
    
    private 
$_offers;

    
/**
     * Gets the list of offers/actions for which the user granted consent.
     */
    
public function getOffers()
    {
        return 
$this->_offers;
    }

    private 
$_offers_string;

    
/**
     * Gets the string representation of all the offers/actions for which 
     * the user granted consent.
     */
    
public function getOffersString()
    {
        return 
$this->_offers_string;
    }

    
/**
     * Sets the offers/actions for which user granted consent.
     */
    
private function setOffers($offers)
    {
        if (!
$offers) {
            throw new 
Exception('Error: WLL_ConsentToken: Null offers.');
        }
        
        
$off_s "";
        
$off = array();

        
$offers urldecode($offers);
        
$offers explode(";"$offers);
        foreach (
$offers as $offer) {
            
$offer explode(":"$offer);
            
$offer $offer[0];
            if (
$off_s) {
                
$off_s .= ",";
            }
            
$off_s .= $offer;
            
$off[] = $offer;
        }

        
$this->_offers_string $off_s;
        
$this->_offers $off;
    }
    
    private 
$_locationID;
    
/**
     * Gets the location ID.
     */
    
public function getLocationID()
    {
        return 
$this->_locationID;
    }

    
/**
     * Sets the location ID.
     */
    
private function setLocationID($locationID)
    {
        if (!
$locationID) {
            throw new 
Exception('Error: WLL_ConsentToken: Null Location ID.');
        }    
        
$this->_locationID $locationID;
    }

    private 
$_context;
    
/**
     * Returns the application context that was originally passed
     * to the sign-in request, if any.
     */
    
public function getContext()
    {
        return 
$this->_context;
    }

    
/**
     * Sets the application context.
     */
    
private function setContext($context)
    {
        
$this->_context $context;
    }

    private 
$_decodedtoken;
    
/**
     * Gets the decoded token.
     */
    
public function getDecodedToken()
    {
        return 
$this->_decodedtoken;
    }

    
/**
     * Sets the decoded token.
     */
    
private function setDecodedToken($decodedtoken)
    {
        
$this->_decodedtoken $decodedtoken;
    }

    private 
$_token;

    
/**
     * Gets the raw token.
     */
    
public function getToken()
    {
        return 
$this->_token;
    }

    
/**
     * Sets the raw token.
     */
    
private function setToken($token)
    {
        
$this->_token $token;
    }

    
/**
     * Makes a copy of the ConsentToken object.
     */
    
private function copy($ct)
    {
        
$this->_delegationtoken $ct->_delegationtoken;
        
$this->_refreshtoken $ct->_refreshtoken;
        
$this->_sessionkey $ct->_sessionkey;
        
$this->_expiry $ct->_expiry;
        
$this->_offers $ct->_offers;
        
$this->_offers_string $ct->_offers_string;
        
$this->_locationID $ct->_locationID;
        
$this->_decodedtoken $ct->_decodedtoken;
        
$this->_token $ct->_token;
    }
}

class 
WindowsLiveLogin
{
    
/* Implementation of basic methods for Web Authentication support. */

    
private $_debug false;

    
/**
     * Stub implementation for logging errors. If you want to enable
     * debugging output, set this to true. In this implementation
     * errors will be logged using the PHP error_log function.
     */
    
public function setDebug($debug)
    {
        
$this->_debug $debug;
    }

    
/**
     * Stub implementation for logging errors. By default, this
     * function does nothing if the debug flag has not been set with
     * setDebug. Otherwise, errors are logged using the PHP error_log
     * function.
     */
    
private function debug($string)
    {
        if (
$this->_debug) {
            
error_log($string);
        }
    }
    
    
/**
     * Stub implementation for handling a fatal error.
     */
    
private function fatal($string)
    {
        
self::debug($string);
        throw new 
Exception($string);
    }
    
    
/**
     * Initialize the WindowsLiveLogin module with the application ID,
     *   secret key, and security algorithm.  
     *
     *  We recommend that you employ strong measures to protect the
     *  secret key. The secret key should never be exposed to the Web
     *  or other users.
     *
     *  Be aware that if you do not supply these settings at
     *  initialization time, you may need to set the corresponding
     *  properties manually.
     *
     *  For Delegated Authentication, you may optionally specify the
     *  privacy policy URL and return URL. If you do not specify these
     *  values here, the default values that you specified when you
     *  registered your application will be used.  
     *
     *  The 'force_delauth_nonprovisioned' flag also indicates whether
     *  your application is registered for Delegated Authentication 
     *  (that is, whether it uses an application ID and secret key). We 
     *  recommend that your Delegated Authentication application always 
     *  be registered for enhanced security and functionality.
     */
    
public function __construct($appid=null$secret=null$securityalgorithm=null,
                                
$force_delauth_nonprovisioned=null,
                                
$policyurl=null$returnurl=null)
    {
        
self::setForceDelAuthNonProvisioned($force_delauth_nonprovisioned);
        
        if (
$appid) {
            
self::setAppId($appid);
        }
        if (
$secret) {
            
self::setSecret($secret);
        }
        if (
$securityalgorithm) {
            
self::setSecurityAlgorithm($securityalgorithm);
        }
        if (
$policyurl) {
            
self::setPolicyUrl($policyurl);
        }
        if (
$returnurl) {
            
self::setReturnUrl($returnurl);
        }
    }

    
/**
     * Initialize the WindowsLiveLogin module from a settings file. 
     *
     *  'settingsFile' specifies the location of the XML settings file
     *  that contains the application ID, secret key, and security
     *  algorithm. The file is of the following format:
     *
     *  <windowslivelogin>
     *    <appid>APPID</appid>
     *    <secret>SECRET</secret>
     *    <securityalgorithm>wsignin1.0</securityalgorithm>
     *  </windowslivelogin>
     *
     *  In a Delegated Authentication scenario, you may also specify
     *  'returnurl' and 'policyurl' in the settings file, as shown in the
     *  Delegated Authentication samples.
     *
     *  We recommend that you store the WindowsLiveLogin settings file
     *  in an area on your server that cannot be accessed through the 
     *  Internet. This file contains important confidential information.
     */
    
public static function initFromXml($settingsFile)
    {
        
$o = new WindowsLiveLogin();
        
$settings $o->parseSettings($settingsFile);
        
        if (@
$settings['debug'] == 'true') {
            
$o->setDebug(true);
        }
        else {
            
$o->setDebug(false);
        }

        if (@
$settings['force_delauth_nonprovisioned'] == 'true') {
            
$o->setForceDelAuthNonProvisioned(true);
        }
        else {
            
$o->setForceDelAuthNonProvisioned(false);
        }

        
$o->setAppId(@$settings['appid']);
        
$o->setSecret(@$settings['secret']);
        
$o->setOldSecret(@$settings['oldsecret']);
        
$o->setOldSecretExpiry(@$settings['oldsecretexpiry']);
        
$o->setSecurityAlgorithm(@$settings['securityalgorithm']);
        
$o->setPolicyUrl(@$settings['policyurl']);
        
$o->setReturnUrl(@$settings['returnurl']);        
        
$o->setBaseUrl(@$settings['baseurl']);
        
$o->setSecureUrl(@$settings['secureurl']);
        
$o->setConsentBaseUrl(@$settings['consenturl']);
        return 
$o;
    }

    private 
$_appid;

    
/**
     * Sets the application ID. Use this method if you did not specify
     * an application ID at initialization.
     */
    
public function setAppId($appid)
    {
        
$_force_delauth_nonprovisioned $this->_force_delauth_nonprovisioned;
        if (!
$appid) {
            if (
$_force_delauth_nonprovisioned) {
                return;
            }
            
self::fatal('Error: setAppId: Null application ID.');
        }
        if (!
preg_match('/^w+$/'$appid)) {
            
self::fatal("Error: setAppId: Application ID must be alpha-numeric: $appid");
        }
        
$this->_appid $appid;
    }

    
/**
     * Returns the application ID.
     */
    
public function getAppId()
    {
        if (!
$this->_appid) {
            
self::fatal('Error: getAppId: Application ID was not set. Aborting.');
        }
        return 
$this->_appid;
    }

    private 
$_signkey;
    private 
$_cryptkey;
    
    
/**
     * Sets your secret key. Use this method if you did not specify
     * a secret key at initialization.
     */
    
public function setSecret($secret)
    {
        
$_force_delauth_nonprovisioned $this->_force_delauth_nonprovisioned;
        if (!
$secret || (strlen($secret) < 16)) {
            if (
$_force_delauth_nonprovisioned) {
                return;
            }
            
self::fatal("Error: setSecret: Secret key is expected to be non-null and longer than 16 characters.");
        }
        
        
$this->_signkey  self::derive($secret"SIGNATURE");
        
$this->_cryptkey self::derive($secret"ENCRYPTION");
    }

    private 
$_oldsignkey;
    private 
$_oldcryptkey;

    
/**
     * Sets your old secret key.
     *
     * Use this property to set your old secret key if you are in the
     * process of transitioning to a new secret key. You may need this 
     * property because the Windows Live ID servers can take up to 
     * 24 hours to propagate a new secret key after you have updated 
     * your application settings.
     *
     * If an old secret key is specified here and has not expired
     * (as determined by the oldsecretexpiry setting), it will be used
     * as a fallback if token decryption fails with the new secret 
     * key.
     */
    
public function setOldSecret($secret)
    {
        if (!
$secret) {
            return;
        }
        if (
strlen($secret) < 16) {
            
self::fatal("Error: setOldSecret: Secret key is expected to be non-null and longer than 16 characters.");
        }
        
        
$this->_oldsignkey  self::derive($secret"SIGNATURE");
        
$this->_oldcryptkey self::derive($secret"ENCRYPTION");
    }

    private 
$_oldsecretexpiry;

    
/**
     * Sets the expiry time for your old secret key.
     *
     * After this time has passed, the old secret key will no longer be
     * used even if token decryption fails with the new secret key.
     *
     * The old secret expiry time is represented as the number of seconds
     * elapsed since January 1, 1970. 
     */
    
public function setOldSecretExpiry($timestamp)
    {
        if (!
$timestamp) {
            return;
        }

        if (!
preg_match('/^d+$/'$timestamp) || ($timestamp <= 0)) {
            
self::fatal('Error: setOldSecretExpiry Invalid timestamp: '
                        
$timestamp);
        }
        
        
$this->_oldsecretexpiry $timestamp;
    }

    
/**
     * Gets the old secret key expiry time.
     */
    
public function getOldSecretExpiry()
    {
        return 
$this->_oldsecretexpiry;
    }

    private 
$_securityalgorithm;

    
/**
     * Sets the version of the security algorithm being used.
     */
    
public function setSecurityAlgorithm($securityalgorithm)
    {
        
$this->_securityalgorithm $securityalgorithm;
    }

    
/**
     * Gets the version of the security algorithm being used.
     */
    
public function getSecurityAlgorithm()
    {
        
$securityalgorithm $this->_securityalgorithm;
        if (!
$securityalgorithm) {
            return 
'wsignin1.0';
        }
        return 
$securityalgorithm;
    }

    private 
$_force_delauth_nonprovisioned;

    
/**
     * Sets a flag that indicates whether Delegated Authentication
     * is non-provisioned (i.e. does not use an application ID or secret
     * key).
     */
    
public function setForceDelAuthNonProvisioned($force_delauth_nonprovisioned)
    {
        
$this->_force_delauth_nonprovisioned $force_delauth_nonprovisioned;
    }

    private 
$_policyurl;

    
/**
     * Sets the privacy policy URL if you did not provide one at initialization time.
     */
    
public function setPolicyUrl($policyurl)
    {
        
$_force_delauth_nonprovisioned $this->_force_delauth_nonprovisioned;
        if (!
$policyurl) {
            if (
$_force_delauth_nonprovisioned) {
                
self::fatal("Error: setPolicyUrl: Null policy URL given.");
            }
        }
        
$this->_policyurl $policyurl;
    }

    
/**
     * Gets the privacy policy URL for your site.
     */
    
public function getPolicyUrl()
    {
        
$policyurl $this->_policyurl;
        
$_force_delauth_nonprovisioned $this->_force_delauth_nonprovisioned;
        if (!
$policyurl) {
            
self::debug("Warning: In the initial release of Delegated Auth, a Policy URL must be configured in the SDK for both provisioned and non-provisioned scenarios.");
            if (
$_force_delauth_nonprovisioned) {
                
self::fatal("Error: getPolicyUrl: Policy URL must be set in a Del Auth non-provisioned scenario. Aborting.");
            }
        }
        return 
$policyurl;
    }

    private 
$_returnurl;

    
/**
     * Sets the return URL--the URL on your site to which the consent 
     *  service redirects users (along with the action, consent token, 
     *  and application context) after they have successfully provided 
     *  consent information for Delegated Authentication. This value will 
     *  override the return URL specified during registration.
     */
    
public function setReturnUrl($returnurl)
    {
        
$_force_delauth_nonprovisioned $this->_force_delauth_nonprovisioned;
        if (!
$returnurl) {
            if (
$_force_delauth_nonprovisioned) {
                
self::fatal("Error: setReturnUrl: Null return URL given.");
            }
        }
        
$this->_returnurl $returnurl;
    }

    
/**
     * Returns the return URL of your site.
     */
    
public function getReturnUrl()
    {
        
$_force_delauth_nonprovisioned $this->_force_delauth_nonprovisioned;
        
$returnurl $this->_returnurl;
        if (!
$returnurl) {
            if (
$_force_delauth_nonprovisioned) {
                
self::fatal("Error: getReturnUrl: Return URL must be set in a Del Auth non-provisioned scenario. Aborting.");
            }
        }
        return 
$returnurl;
    }

    private 
$_baseurl;

    
/**
     * Sets the base URL to use for the Windows Live Login server. 
     *  You should not have to change this property. Furthermore, we recommend 
     *  that you use the Sign In control instead of the URL methods
     *  provided here.
     */
    
public function setBaseUrl($baseurl
    {
        
$this->_baseurl $baseurl;
    }

    
/**
     * Gets the base URL to use for the Windows Live Login server. 
     * You should not have to use this property. Furthermore, we recommend 
     * that you use the Sign In control instead of the URL methods 
     * provided here.
     */
    
public function getBaseUrl() 
    {
        
$baseurl $this->_baseurl;
        if (!
$baseurl) {
            return 
"http://login.live.com/";
        }
        return 
$baseurl;
    }

    private 
$_secureurl;

    
/**
     * Sets the secure (HTTPS) URL to use for the Windows Live Login 
     * server. You should not have to change this property.
     */
    
public function setSecureUrl($secureurl
    {
        
$this->_secureurl $secureurl;
    }

    
/**
     * Gets the secure (HTTPS) URL to use for the Windows Live Login 
     * server. You should not have to use this functon directly.
     */
    
public function getSecureUrl() 
    {
        
$secureurl $this->_secureurl;
        if (!
$secureurl) {
            return 
"https://login.live.com/";
        }
        return 
$secureurl;
    }

    private 
$_consenturl;

    
/**
     * Sets the Consent Base URL to use for the Windows Live Consent 
     * server. You should not have to use or change this property directly.
     */
    
public function setConsentBaseUrl($consenturl
    {
        
$this->_consenturl $consenturl;
    }

    
/**
     * Gets the URL to use for the Windows Live Consent server. You
     * should not have to use or change this directly.
     */
    
public function getConsentBaseUrl() 
    {
        
$consenturl $this->_consenturl;
        if (!
$consenturl) {
            return 
"https://consent.live.com/";
        }
        return 
$consenturl;
    }

    
/* Methods for Web Authentication support. */

    /**
     * Returns the sign-in URL to use for the Windows Live Login server. 
     * We recommend that you use the Sign In control instead.
     *   
     * If you specify it, 'context' will be returned as-is in the sign-in
     * response for site-specific use.     
     */
    
public function getLoginUrl($context=null$market=null)
    {
        
$url  self::getBaseUrl(); 
        
$url .= 'wlogin.srf?appid=' self::getAppId();
        
$url .= '&alg=' self::getSecurityAlgorithm();
        
$url .= ($context '&appctx=' urlencode($context) : '');
        
$url .= ($market '&mkt=' urlencode($market) : '');
        return 
$url;
    }
    
    
/**
     * Returns the sign-out URL to use for the Windows Live Login server. 
     * We recommend that you use the Sign In control instead.
     */
    
public function getLogoutUrl($market=null)
    {
        
$url self::getBaseUrl();
        
$url .= "logout.srf?appid=" self::getAppId();
        
$url .= ($market '&mkt=' urlencode($market) : '');
        return 
$url;
    }

    
/**
     * Processes the sign-in response from Windows Live Login server.
     * 
     * @param query contains the preprocessed POST query, a map of
     *              Strings to an an array of Strings, such as that 
     *              returned by ServletRequest.getParameterMap().
     * @return      a User object on successful sign-in; otherwise null.
     */
    
public function processLogin($query)
    {        
        
$action = @$query['action'];
        if (
$action != 'login') {
            
self::debug("Warning: processLogin: query action ignored: $action");
            return;
        }
        
$token  = @$query['stoken'];
        
$context urldecode(@$query['appctx']);
        return 
self::processToken($token$context);
    }

    
/**
     * Decodes and validates a Web Authentication token. Returns a User 
     * object on success. If a context is passed in, it will be returned 
     * as the context field in the User object.
     */
    
public function processToken($token$context=null)
    {
        if (!
$token) {
            
self::debug('Error: processToken: Invalid token specified.');
            return;
        }

        
$decodedToken self::decodeAndValidateToken($token);
        if (!
$decodedToken) {
            
self::debug("Error: processToken: Failed to decode/validate token: $token");
            return;
        }

        
$parsedToken self::parse($decodedToken);
        if (!
$parsedToken) {
            
self::debug("Error: processToken: Failed to parse token after decoding: $token");
            return;
        }
        
        
$appid self::getAppId();
        
$tokenappid = @$parsedToken['appid'];
        if (
$appid != $tokenappid) {
            
self::debug("Error: processToken: Application ID in token did not match ours: $tokenappid$appid");
            return;
        }

        
$user null;

        try {
            
$user = new WLL_User(@$parsedToken['ts'], 
                                 @
$parsedToken['uid'], 
                                 @
$parsedToken['flags'], 
                                 
$context$token);
        } catch (
Exception $e) {
            
self::debug("Error: processToken: Contents of token considered invalid: " $e->getMessage());
        }
        
        return 
$user;
    }
    
    
/**
     * Returns an appropriate content type and body response that the 
     * application handler can return to signify a successful sign-out 
     * from the application.
     *  
     * When a user signs out of Windows Live or a Windows Live
     * application, a best-effort attempt is made at signing the user out
     * from all other Windows Live applications the user might be signed
     * in to. This is done by calling the handler page for each
     * application with 'action' set to 'clearcookie' in the query
     * string. The application handler is then responsible for clearing
     * any cookies or data associated with the sign-in. After successfully
     * signing the user out, the handler should return a GIF (any GIF)
     * image as response to the 'action=clearcookie' query.
     */
    
public function getClearCookieResponse()
    {
        
$type "image/gif";
        
$content "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAIBTAA7";
        
$content base64_decode($content);
        return array(
$type$content);
    }

    
/* Methods for Delegated Authentication. */

    /*
     * Returns the consent URL to use for Delegated Authentication for
     * the given comma-delimited list of offers.
     *
     * If you specify it, 'context' will be returned as-is in the consent
     * response for site-specific use.
     *
     * The registered/configured return URL can also be overridden by 
     * specifying 'ru' here.
     *
     * You can change the language in which the consent page is displayed
     * by specifying a culture ID (For example, 'fr-fr' or 'en-us') in the
     * 'market' parameter.
     */
    
public function getConsentUrl($offers$context=null$ru=null$market=null)
    {
        if (!
$offers) {
            throw new 
Exception('Error: getConsentUrl: Invalid offers list.');
        }
        
$url  self::getConsentBaseUrl(); 
        
$url .= 'Delegation.aspx?ps=' urlencode($offers);
        
$ru = ($ru $ru self::getReturnUrl());
        
$url .= ($ru '&ru=' urlencode($ru) : '');
        
$pl self::getPolicyUrl();
        
$url .= ($pl '&pl=' urlencode($pl) : '');
        
$url .= ($market '&mkt=' urlencode($market) : '');
        if (!
$this->_force_delauth_nonprovisioned) {
            
$url .= '&app=' self::getAppVerifier();
        }
        
$url .= ($context '&appctx=' urlencode($context) : '');
        return 
$url;
    }

    
/*
     * Returns the URL to use to download a new consent token, given the 
     * offers and refresh token.
     *
     * The registered/configured return URL can also be overridden by 
     * specifying 'ru' here.      
     */
    
public function getRefreshConsentTokenUrl($offers$refreshtoken$ru=null)
    {
        
$_force_delauth_nonprovisioned $this->_force_delauth_nonprovisioned;
        if (!
$offers) {
            throw new 
Exception('Error: getRefreshConsentTokenUrl: Invalid offers list.');
        }
        if (!
$refreshtoken) {
            throw new 
Exception('Error: getRefreshConsentTokenUrl: Invalid refresh token.');
        }

        
$url  self::getConsentBaseUrl(); 
        
$url .= 'RefreshToken.aspx?ps=' urlencode($offers);
        
$url .= '&reft=' $refreshtoken;
        
$ru = ($ru $ru self::getReturnUrl());
        
$url .= ($ru '&ru=' urlencode($ru) : '');

        if (!
$this->_force_delauth_nonprovisioned) {
            
$url .= '&app=' self::getAppVerifier();
        }

        return 
$url;
    }

    
/*
     * Returns the URL for the consent-management user interface.
     *
     * You can change the language in which the consent page is displayed
     * by specifying a culture ID (For example, 'fr-fr' or 'en-us') in the
     * 'market' parameter.     
     */
    
public function getManageConsentUrl($market=null
    {
        
$url  self::getConsentBaseUrl(); 
        
$url .= 'ManageConsent.aspx';
        
$url .= ($market '?mkt=' urlencode($market) : '');
        return 
$url;
    }

    
/*
     * Processes the POST response from the Delegated Authentication 
     * service after a user has granted consent. The processConsent
     * function extracts the consent token string and returns the result 
     * of invoking the processConsentToken method. 
     */
    
public function processConsent($query)
    {        
        
$action = @$query['action'];
        if (
$action != 'delauth') {
            
self::debug("Warning: processConsent: query action ignored: $action");
            return;
        }
        
$responsecode = @$query['ResponseCode'];
        if (
$responsecode != 'RequestApproved') {
            
self::debug("Warning: processConsent: consent was not successfully granted: $responsecode");
            return;
        }
        
$token  = @$query['ConsentToken'];
        
$context urldecode(@$query['appctx']);
        return 
self::processConsentToken($token$context);
    }

    
/*
     * Processes the consent token string that is returned in the POST 
     * response by the Delegated Authentication service after a 
     * user has granted consent.
     */
    
public function processConsentToken($token$context=null)
    {
        if (!
$token) {
            
self::debug('Error: processConsentToken: Null token.');
            return;
        }
        
        
$decodedToken $token;
        
$parsedToken self::parse(urldecode($decodedToken));
        if (!
$parsedToken) {
            
self::debug("Error: processConsentToken: Failed to parse token: $token");
            return;
        }

        
$eact = @$parsedToken['eact'];
        if (
$eact) {
            
$decodedToken self::decodeAndValidateToken($eact);
            if (!
$decodedToken) {
                
self::debug("Error: processConsentToken: Failed to decode/validate token: $token");
                return;
            }
            
$parsedToken self::parse($decodedToken);
            if (!
$parsedToken) {
                
self::debug("Error: processConsentToken: Failed to parse token after decoding: $token");
                return;
            }
            
$decodedToken urlencode($decodedToken);
        }
        
        
$consenttoken null;

        try {
            
$consenttoken = new WLL_ConsentToken($this,
                                                @
$parsedToken['delt'], 
                                                @
$parsedToken['reft'], 
                                                @
$parsedToken['skey'], 
                                                @
$parsedToken['exp'], 
                                                @
$parsedToken['offer'], 
                                                @
$parsedToken['lid'],
                                                
$context$decodedToken$token);
        } catch (
Exception $e) {
            
self::debug("Error: processConsentToken: Contents of token considered invalid: " $e->getMessage());
        }
        return 
$consenttoken;
    }

    
/*
     * Attempts to obtain a new, refreshed token and return it. The 
     * original token is not modified.
     */
    
public function refreshConsentToken($token$ru=null)
    {
        if (!
$token) {
            
self::debug("Error: refreshConsentToken: Null consent token.");
            return;
        }
        
self::refreshConsentToken2($token->getOffersString(), $token->getRefreshToken(), $ru);
    }

    
/*
     * Helper function to obtain a new, refreshed token and return it.
     * The original token is not modified.
     */
    
public function refreshConsentToken2($offers_string$refreshtoken$ru=null)
    {
        
$body self::fetch(self::getRefreshConsentTokenUrl($offers_string$refreshtoken$ru));
        if (!
$body) {
            
self::debug("Error: refreshConsentToken2: Failed to obtain a new token.");
            return;
        }
            
        
preg_match('/{"ConsentToken":"(.*)"}/'$body$matches);
        if(
count($matches) == 2) {
            return 
$matches[1];
        }
        else {
            
self::debug("Error: refreshConsentToken2: Failed to extract token: $body");
            return;
        }
    }

    
/* Common methods. */

    /*
     * Decodes and validates the token.
     */
    
public function decodeAndValidateToken($token$cryptkey=null$signkey=null,
                                           
$internal_allow_recursion=true)
    {
        if (!
$cryptkey) {
            
$cryptkey $this->_cryptkey;
        }
        if (!
$signkey) {
            
$signkey $this->_signkey;
        }

        
$haveoldsecret false;
        
$oldsecretexpiry self::getOldSecretExpiry();
        
$oldcryptkey $this->_oldcryptkey;
        
$oldsignkey $this->_oldsignkey;

        if (
$oldsecretexpiry and (time() < $oldsecretexpiry)) {
            if (
$oldcryptkey and $oldsignkey) {
                
$haveoldsecret true;
            }
        }
        
$haveoldsecret = ($haveoldsecret and $internal_allow_recursion);
        
        
$stoken self::decodeToken($token$cryptkey);

        if (
$stoken) {
            
$stoken self::validateToken($stoken$signkey);
        }

        if (!
$stoken and $haveoldsecret) {
            
self::debug("Warning: Failed to validate token with current secret, attempting old secret.");
            
$stoken 
              
self::decodeAndValidateToken($token$oldcryptkey$oldsignkeyfalse);
        }

        return 
$stoken;
    }

    
/**
     * Decodes the given token string; returns undef on failure.
     *
     * First, the string is URL-unescaped and base64 decoded.
     * Second, the IV is extracted from the first 16 bytes of the string.
     * Finally, the string is decrypted using the encryption key.
     */
    
public function decodeToken($token$cryptkey=null)
    {
        if (!
$cryptkey) {
            
$cryptkey $this->_cryptkey;
        }
        if (!
$cryptkey) {
            
self::fatal("Error: decodeToken: Secret key was not set. Aborting.");
        }
        
        
$ivLen 16;
        
$token self::u64($token);
        
$len strlen($token);
        
        if (!
$token || ($len <= $ivLen) || (($len $ivLen) != 0)) {
            
self::debug("Error: decodeToken: Attempted to decode invalid token.");
            return;
        }
        
        
$iv      substr($token016);
        
$crypted substr($token16);
        
$mode    MCRYPT_MODE_CBC
        
$enc     MCRYPT_RIJNDAEL_128;
        return 
mcrypt_decrypt($enc$cryptkey$crypted$mode$iv);
    }

    
/**
     * Creates a signature for the given string by using the signature
     * key.
     */
    
public function signToken($token$signkey=null)
    {
        if (!
$signkey) {
            
$signkey $this->_signkey;
        }
        if (!
$signkey) {
            
self::fatal("Error: signToken: Secret key was not set. Aborting.");
        }
        
        if (!
$token) {
            
self::debug("Attempted to sign null token.");
            return;
        }

        return 
hash_hmac("sha256"$token$signkeytrue);
    }

    
/**
     * Extracts the signature from the token and validates it.
     */
    
public function validateToken($token$signkey=null)
    {
        if (!
$signkey) {
            
$signkey $this->_signkey;
        }
        if (!
$token) {
            
self::debug("Error: validateToken: Invalid token.");
            return;
        }

        
$split explode("&sig="$token);
        if (
count($split) != 2) {
            
self::debug("ERROR: validateToken: Invalid token: $token");
            return;
        }
        list(
$body$sig) = $split;

        
$sig self::u64($sig);
        if (!
$sig) {
            
self::debug("Error: validateToken: Could not extract signature from token.");
            return;
        }

        
$sig2 self::signToken($body$signkey);
        if (!
$sig2) {
            
self::debug("Error: validateToken: Could not generate signature for the token.");
            return;
        }
        
          
        if (
$sig == $sig2) {
            return 
$token;    
        }
        
        
self::debug("Error: validateToken: Signature did not match.");
        return;
    }

    
/* Implementation of the methods needed to perform Windows Live
       application verification as well as trusted sign-in. */

    /**
     * Generates an application verifier token. An IP address can 
     * optionally be included in the token.
     */
    
public function getAppVerifier($ip=null)
    {
        
$token  'appid=' self::getAppId() . '&ts=' self::getTimestamp();
        
$token .= ($ip "&ip={$ip}'');
        
$token .= '&sig=' self::e64(self::signToken($token));
        return 
urlencode($token);
    }

    
/**
     * Returns the URL that is required to retrieve the application 
     * security token.
     *
     * By default, the application security token is generated for 
     * the Windows Live site; a specific Site ID can optionally be 
     * specified in 'siteid'. The IP address can also optionally be 
     * included in 'ip'.
     *
     * If 'js' is nil, a JavaScript Output Notation (JSON) response is 
     * returned in the following format: 
     *
     *  {"token":"<value>"}
     *
     * Otherwise, a JavaScript response is returned. It is assumed that
     * WLIDResultCallback is a custom function implemented to handle the
     * token value:
     *
     * WLIDResultCallback("<tokenvalue>");
     */
    
public function getAppLoginUrl($siteid=null$ip=null$js=null)
    {
        
$url  self::getSecureUrl();
        
$url .= 'wapplogin.srf?app=' self::getAppVerifier($ip);
        
$url .= '&alg=' self::getSecurityAlgorithm();
        
$url .= ($siteid "&id=$siteid'');
        
$url .= ($js '&js=1' '');
        return 
$url;
    }

    
/**
     * Retrieves the application security token for application
     * verification from the application sign-in URL.  
     *
     * By default, the application security token will be generated for
     * the Windows Live site; a specific Site ID can optionally be
     * specified in 'siteid'. The IP address can also optionally be
     * included in 'ip'.
     *
     * Implementation note: The application security token is downloaded
     * from the application sign-in URL in JSON format:
     *
     * {"token":"<value>"}
     *
     * Therefore we must extract <value> from the string and return it as
     *  seen here.
     */
    
public function getAppSecurityToken($siteid=null$ip=null)
    {
        
$body self::fetch(self::getAppLoginUrl($siteid$ip));
        if (!
$body) {
            
self::debug("Error: getAppSecurityToken: Could not fetch the application security token.");
            return;
        }
            
        
preg_match('/{"token":"(.*)"}/'$body$matches);
        if(
count($matches) == 2) {
            return 
$matches[1];
        }
        else {
            
self::debug("Error: getAppSecurityToken: Failed to extract token: $body");
            return;
        }
    }

    
/**
     * Returns a string that can be passed to the getTrustedParams
     * function as the 'retcode' parameter. If this is specified as the
     * 'retcode', the application will be used as return URL after it
     * finishes trusted sign-in.
     */
    
public function getAppRetCode()
    {    
        return 
'appid=' self::getAppId();
    }

    
/**
     * Returns a table of key-value pairs that must be posted to the
     * sign-in URL for trusted sign-in. Use HTTP POST to do this. Be aware
     * that the values in the table are neither URL nor HTML escaped and
     * may have to be escaped if you are inserting them in code such as
     * an HTML form.
     *
     * The user to be trusted on the local site is passed in as string 
     * 'user'.
     *
     *  Optionally, 'retcode' specifies the resource to which successful
     *  sign-in is redirected, such as Windows Live Mail, and is typically
     *  a string in the format 'id=2000'. If you pass in the value from
     *  getAppRetCode instead, sign-in will be redirected to the 
     *  application. Otherwise, an HTTP 200 response is returned.
     */
    
public function getTrustedParams($user$retcode=null)
    {
        
$token  self::getTrustedToken($user);
        if (!
$token) {
            return;
        }
        
$token "<wst:RequestSecurityTokenResponse xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust"><wst:RequestedSecurityToken><wsse:BinarySecurityToken xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">$token</wsse:BinarySecurityToken></wst:RequestedSecurityToken><wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"><wsa:EndpointReference xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"><wsa:Address>uri:WindowsLiveID</wsa:Address></wsa:EndpointReference></wsp:AppliesTo></wst:RequestSecurityTokenResponse>";
        
        
$params = array();
        
$params['wa']      = self::getSecurityAlgorithm();
        
$params['wresult'] = $token;
        
        if (
$retcode) {
            
$params['wctx'] = $retcode;
        }
        
        return 
$params;        
    }

    
/**
     * Returns the trusted sign-in token in the format that is needed by a
     * control doing trusted sign-in.
     *
     * The user to be trusted on the local site is passed in as string
     * 'user'.
     */
    
public function getTrustedToken($user)
    {
        if (!
$user) {
            
self::debug('Error: getTrustedToken: Null user specified.');
            return;
        }
        
        
$token  "appid=" self::getAppId() . "&uid=" urlencode($user
          . 
"&ts="self::getTimestamp();
        
$token .= "&sig="  self::e64(self::signToken($token));
        return 
urlencode($token);
    }

    
/**
     * Returns the trusted sign-in URL to use for Windows Live Login server.
     */
    
public function getTrustedLoginUrl()
    {
        return 
self::getSecureUrl() . 'wlogin.srf';
    }
    
    
/**
     * Returns the trusted sign-in URL to use for Windows Live
     *  Login server.
     */
    
public function getTrustedLogoutUrl()
    {
        return 
self::getSecureUrl() . "logout.srf?appid=" self::getAppId();
    }

    
/* Helper methods */
    
    /**
     * Function to parse the settings file.
     */
    
private function parseSettings($settingsFile)
    {
        
$settings = array();
        
$doc = new DOMDocument();
        if (!
$doc->load($settingsFile)) {
            
self::fatal("Error: parseSettings: Error while reading $settingsFile");
        }
        
        
$nl $doc->getElementsByTagName('windowslivelogin');
        if(
$nl->length != 1) {
            
self::fatal("error: parseSettings: Failed to parse settings file:" 
                        
$settingsFile);
        }

        
$topnode $nl->item(0);
        foreach (
$topnode->childNodes as $node) {
            if (
$node->nodeType == XML_ELEMENT_NODE) {
                
$firstChild $node->firstChild;
                if (!
$firstChild) {
                    
self::fatal("error: parseSettings: Failed to parse settings file:" 
                                
$settingsFile);
                }
                
$settings[$node->nodeName] = $firstChild->nodeValue;
            }
        }
        
        return 
$settings;
    }
    
    
/**
     * Derives the key, given the secret key and prefix as described in the
     * Web Authentication SDK documentation.
     */
    
private function derive($secret$prefix)
    {
        if (!
$secret || !$prefix) {
            
self::fatal("Error: derive: secret or prefix is null.");
        }        

        
$keyLen 16;
        
$key $prefix $secret;
        
$key mhash(MHASH_SHA256$key);
        if (!
$key || (strlen($key) < $keyLen)) {
            
self::debug("Error: derive: Unable to derive key.");
            return;
        }
        
        return 
substr($key0$keyLen);
    }

    
/**
     * Parses query string and returns a hash.
     *
     * If a hash ref is passed in from CGI->Var, it is dereferenced and
     * returned.
     */
    
private function parse($input)
    {                
        if (!
$input) {
            
self::debug("Error: parse: Null input.");
            return;
        }

        
$input explode('&'$input);
        
$pairs = array();
        
        foreach (
$input as $pair) {
            
$kv explode('='$pair);
            if (
count($kv) != 2) {
                
self::debug("Error: parse: Bad input to parse: " $pair);
                return;
            }
            
$pairs[$kv[0]] = $kv[1];
        }
                
        return 
$pairs;
    }

    
/**
     * Generates a time stamp suitable for the application verifier 
     * token.
     */
    
private function getTimestamp()
    {    
        return 
time();
    }

    
/**
     * Base64-encodes and URL-escapes a string.
     */
    
private function e64($input)
    {
        if (
is_null($input)) {
            return;
        }
        return 
urlencode(base64_encode($input));
    }

    
/**
     * URL-unescapes and Base64-decodes a string.
     */
    
private function u64($input)
    {
        if(
is_null($input))
            return;
        return 
base64_decode(urldecode($input));
    }

    
/**
     * Fetches the contents given a URL.
     */
    
private function fetch($url)
    {        
        if (!(
$handle fopen($url"rb"))) {
            
self::debug("error: fetch: Could not open url: $url");
            return;
        }
        
        if (!(
$contents stream_get_contents($handle))) {
            
self::debug("Error: fetch: Could not read from url: $url");
        }
        
        
fclose($handle);
        return 
$contents;
    }
}
?>
Онлайн: 3
Реклама