Skip to content

Commit

Permalink
release
Browse files Browse the repository at this point in the history
git-svn-id: https://plugins.svn.wordpress.org/post-smtp/trunk@1746635 b8457f37-d9ea-0310-8a92-e5e31aec5664
  • Loading branch information
yehudah committed Oct 15, 2017
1 parent 2065680 commit edff34b
Show file tree
Hide file tree
Showing 400 changed files with 243,742 additions and 0 deletions.
168 changes: 168 additions & 0 deletions Postman/Postman-Auth/PostmanAbstractAuthenticationManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
<?php
if (! class_exists ( "PostmanAbstractAuthenticationManager" )) {

require_once 'PostmanAuthenticationManager.php';

/**
*/
abstract class PostmanAbstractAuthenticationManager implements PostmanAuthenticationManager {

// constants
const APPROVAL_PROMPT = 'force';
const ACCESS_TYPE = 'offline';
const ACCESS_TOKEN = 'access_token';
const REFRESH_TOKEN = 'refresh_token';
const EXPIRES = 'expires_in';

// the oauth authorization options
private $clientId;
private $clientSecret;
private $authorizationToken;
private $callbackUri;
private $logger;

/**
* Constructor
*/
public function __construct(PostmanLogger $logger, $clientId, $clientSecret, PostmanOAuthToken $authorizationToken, $callbackUri) {
assert ( ! empty ( $clientId ) );
assert ( ! empty ( $clientSecret ) );
assert ( ! empty ( $authorizationToken ) );
assert ( ! empty ( $callbackUri ) );
$this->logger = $logger;
$this->clientId = $clientId;
$this->clientSecret = $clientSecret;
$this->authorizationToken = $authorizationToken;
$this->callbackUri = $callbackUri;
}
protected function getLogger() {
return $this->logger;
}
protected function getClientId() {
return $this->clientId;
}
protected function getClientSecret() {
return $this->clientSecret;
}
protected function getAuthorizationToken() {
return $this->authorizationToken;
}

/**
* Create a state token to prevent request forgery.
* Store it in the session for later validation.
*/
public function generateRequestTransactionId() {
return $state = md5 ( rand () );
}

/**
*/
public function isAccessTokenExpired() {
$expireTime = ($this->authorizationToken->getExpiryTime () - self::FORCE_REFRESH_X_SECONDS_BEFORE_EXPIRE);
$tokenHasExpired = time () > $expireTime;
$this->logger->debug ( 'Access Token Expiry Time is ' . $expireTime . ', expires_in=' . ($expireTime - time ()) . ', expired=' . ($tokenHasExpired ? 'yes' : 'no') );
return $tokenHasExpired;
}

/**
* Decoded the received token
* This code is identical for Google and Hotmail
*
* @param unknown $response
* @throws Exception
*/
protected function processResponse($response) {
$authToken = json_decode ( stripslashes ( $response ) );
if ($authToken === NULL) {
$this->getLogger ()->error ( $response );
throw new Exception ( $response );
} else if (isset ( $authToken->{'error'} )) {
if (isset ( $authToken->{'error_description'} )) {
$this->getLogger ()->error ( $authToken->{'error'} . ' processing response: ' . $authToken->{'error_description'} );
throw new Exception ( $authToken->{'error_description'} . '(' . $authToken->{'error'} . ')' );
} else {
// Yahoo doesn't give descriptions
$this->getLogger ()->error ( $authToken->{'error'} . ' processing response' );
throw new Exception ( $authToken->{'error'} );
}
} else {
$this->getLogger ()->trace ( 'Processing response:' );
$this->getLogger ()->trace ( $response );
$this->decodeReceivedAuthorizationToken ( $authToken );
}
}

/**
* Parses the authorization token and extracts the expiry time, accessToken,
* and if this is a first-time authorization, a refresh token.
*
* This code is identical for Google and Hotmail
*
* @param unknown $client
*/
protected function decodeReceivedAuthorizationToken($newtoken) {
assert ( ! empty ( $newtoken ) );
assert ( ! empty ( $newtoken->{self::EXPIRES} ) );
assert ( ! empty ( $newtoken->{self::ACCESS_TOKEN} ) );

// update expiry time
if (empty ( $newtoken->{self::EXPIRES} )) {
throw new Exception ( '[expires_in] value is missing from the authentication token' );
}
$newExpiryTime = time () + $newtoken->{self::EXPIRES};
$this->getAuthorizationToken ()->setExpiryTime ( $newExpiryTime );
$this->getLogger ()->debug ( 'Updating Access Token Expiry Time ' );

// update acccess token
if (empty ( $newtoken->{self::ACCESS_TOKEN} )) {
throw new Exception ( '[access_token] value is missing from the authentication token' );
}
$newAccessToken = $newtoken->{self::ACCESS_TOKEN};
$this->getAuthorizationToken ()->setAccessToken ( $newAccessToken );
$this->getLogger ()->debug ( 'Updating Access Token' );

// update refresh token, if there is one
if (isset ( $newtoken->{self::REFRESH_TOKEN} )) {
$newRefreshToken = $newtoken->{self::REFRESH_TOKEN};
$this->getAuthorizationToken ()->setRefreshToken ( $newRefreshToken );
$this->getLogger ()->debug ( 'Updating Refresh Token ' );
}
}

/**
* Given an OAuth provider-specific URL and redirectUri,
* issue an HttpRequest to refresh the access token
*
* This code is identical for Google and Hotmail
*/
public function refreshToken() {
$this->getLogger ()->debug ( 'Refreshing Token' );
$refreshUrl = $this->getTokenUrl ();
$callbackUrl = $this->getCallbackUri ();
assert ( ! empty ( $refreshUrl ) );
assert ( ! empty ( $callbackUrl ) );
// the format of the URL is
// client_id=CLIENT_ID&client_secret=CLIENT_SECRET&redirect_uri=REDIRECT_URI&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
$postvals = array (
'client_id' => $this->getClientId (),
'client_secret' => $this->getClientSecret (),
'redirect_uri' => $callbackUrl,
'grant_type' => 'refresh_token',
'refresh_token' => $this->getAuthorizationToken ()->getRefreshToken ()
);
// example request string
// client_id=0000000603DB0F&redirect_uri=http%3A%2F%2Fwww.contoso.com%2Fcallback.php&client_secret=LWILlT555GicSrIATma5qgyBXebRI&refresh_token=*LA9...//refresh token string shortened for example//...xRoX&grant_type=refresh_token
$response = PostmanUtils::remotePostGetBodyOnly ( $refreshUrl, $postvals );
$this->processResponse ( $response );
}
/**
* (non-PHPdoc)
*
* @see PostmanAuthenticationManager::getCallbackUri()
*/
public function getCallbackUri() {
return $this->callbackUri;
}
}
}
15 changes: 15 additions & 0 deletions Postman/Postman-Auth/PostmanAuthenticationManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
if (! interface_exists ( "PostmanAuthenticationManager" )) {
interface PostmanAuthenticationManager {
const POSTMAN_AUTHORIZATION_IN_PROGRESS = 'request_oauth_permission';
const FORCE_REFRESH_X_SECONDS_BEFORE_EXPIRE = 60;
public function isAccessTokenExpired();
public function refreshToken();
public function generateRequestTransactionId();
public function requestVerificationCode($transactionId);
public function processAuthorizationGrantCode($transactionId);
public function getAuthorizationUrl();
public function getTokenUrl();
public function getCallbackUri();
}
}
55 changes: 55 additions & 0 deletions Postman/Postman-Auth/PostmanAuthenticationManagerFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
if (! class_exists ( "PostmanAuthenticationManagerFactory" )) {

require_once 'PostmanGoogleAuthenticationManager.php';
require_once 'PostmanMicrosoftAuthenticationManager.php';
require_once 'PostmanNonOAuthAuthenticationManager.php';
require_once 'PostmanYahooAuthenticationManager.php';

//
class PostmanAuthenticationManagerFactory {
private $logger;

// singleton instance
public static function getInstance() {
static $inst = null;
if ($inst === null) {
$inst = new PostmanAuthenticationManagerFactory ();
}
return $inst;
}
private function __construct() {
$this->logger = new PostmanLogger ( get_class ( $this ) );
}
public function createAuthenticationManager() {
$transport = PostmanTransportRegistry::getInstance ()->getSelectedTransport ();
return $this->createManager ( $transport );
}
private function createManager(PostmanZendModuleTransport $transport) {
$options = PostmanOptions::getInstance ();
$authorizationToken = PostmanOAuthToken::getInstance ();
$authenticationType = $options->getAuthenticationType ();
$hostname = $options->getHostname ();
$clientId = $options->getClientId ();
$clientSecret = $options->getClientSecret ();
$senderEmail = $options->getMessageSenderEmail ();
$scribe = $transport->getScribe ();
$redirectUrl = $scribe->getCallbackUrl ();
if ($transport->isOAuthUsed ( $options->getAuthenticationType () )) {
if ($transport->isServiceProviderGoogle ( $hostname )) {
$authenticationManager = new PostmanGoogleAuthenticationManager ( $clientId, $clientSecret, $authorizationToken, $redirectUrl, $senderEmail );
} else if ($transport->isServiceProviderMicrosoft ( $hostname )) {
$authenticationManager = new PostmanMicrosoftAuthenticationManager ( $clientId, $clientSecret, $authorizationToken, $redirectUrl );
} else if ($transport->isServiceProviderYahoo ( $hostname )) {
$authenticationManager = new PostmanYahooAuthenticationManager ( $clientId, $clientSecret, $authorizationToken, $redirectUrl );
} else {
assert ( false );
}
} else {
$authenticationManager = new PostmanNonOAuthAuthenticationManager ();
}
$this->logger->debug ( 'Created ' . get_class ( $authenticationManager ) );
return $authenticationManager;
}
}
}
119 changes: 119 additions & 0 deletions Postman/Postman-Auth/PostmanGoogleAuthenticationManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php
if (! class_exists ( "PostmanGoogleAuthenticationManager" )) {

require_once 'PostmanAbstractAuthenticationManager.php';
require_once 'PostmanStateIdMissingException.php';

/**
* https://developers.google.com/accounts/docs/OAuth2WebServer
* https://developers.google.com/gmail/xoauth2_protocol
* https://developers.google.com/gmail/api/auth/scopes
*/
class PostmanGoogleAuthenticationManager extends PostmanAbstractAuthenticationManager implements PostmanAuthenticationManager {

// This endpoint is the target of the initial request. It handles active session lookup, authenticating the user, and user consent.
const GOOGLE_ENDPOINT = 'https://accounts.google.com/o/oauth2/auth';
const GOOGLE_REFRESH = 'https://www.googleapis.com/oauth2/v3/token';

// this scope doesn't work
// Create, read, update, and delete drafts. Send messages and drafts.
const SCOPE_COMPOSE = 'https://www.googleapis.com/auth/gmail.compose';

// this scope doesn't work
// All read/write operations except immediate, permanent deletion of threads and messages, bypassing Trash.
const SCOPE_MODIFY = 'https://www.googleapis.com/auth/gmail.modify';

// Full access to the account, including permanent deletion of threads and messages. This scope should only be requested if your application needs to immediately and permanently delete threads and messages, bypassing Trash; all other actions can be performed with less permissive scopes.
const SCOPE_FULL_ACCESS = 'https://mail.google.com/';
const AUTH_TEMP_ID = 'GOOGLE_OAUTH_TEMP_ID';
const VENDOR_NAME = 'google';
// the sender email address
private $senderEmail;

/**
* Constructor
*
* Get a Client ID from https://account.live.com/developers/applications/index
*/
public function __construct($clientId, $clientSecret, PostmanOAuthToken $authorizationToken, $callbackUri, $senderEmail) {
assert ( ! empty ( $clientId ) );
assert ( ! empty ( $clientSecret ) );
assert ( ! empty ( $authorizationToken ) );
assert ( ! empty ( $senderEmail ) );
$logger = new PostmanLogger ( get_class ( $this ) );
$this->senderEmail = $senderEmail;
parent::__construct ( $logger, $clientId, $clientSecret, $authorizationToken, $callbackUri );
}

/**
* The authorization sequence begins when your application redirects a browser to a Google URL;
* the URL includes query parameters that indicate the type of access being requested.
*
* As in other scenarios, Google handles user authentication, session selection, and user consent.
* The result is an authorization code, which Google returns to your application in a query string.
*
* (non-PHPdoc)
*
* @see PostmanAuthenticationManager::requestVerificationCode()
*/
public function requestVerificationCode($transactionId) {
$params = array (
'response_type' => 'code',
'redirect_uri' => urlencode ( $this->getCallbackUri () ),
'client_id' => $this->getClientId (),
'scope' => urlencode ( self::SCOPE_FULL_ACCESS ),
'access_type' => 'offline',
'approval_prompt' => 'force',
'state' => $transactionId,
'login_hint' => $this->senderEmail
);

$authUrl = $this->getAuthorizationUrl () . '?' . build_query ( $params );

$this->getLogger ()->debug ( 'Requesting verification code from Google' );
PostmanUtils::redirect ( $authUrl );
}

/**
* After receiving the authorization code, your application can exchange the code
* (along with a client ID and client secret) for an access token and, in some cases,
* a refresh token.
*
* This code is identical for Google and Hotmail
*
* @see PostmanAuthenticationManager::processAuthorizationGrantCode()
*/
public function processAuthorizationGrantCode($transactionId) {
if (isset ( $_GET ['code'] )) {
$this->getLogger ()->debug ( 'Found authorization code in request header' );
$code = filter_input( INPUT_GET, 'code', FILTER_SANITIZE_STRING );
if (isset ( $_GET ['state'] ) && $_GET ['state'] == $transactionId) {
$this->getLogger ()->debug ( 'Found valid state in request header' );
} else {
$this->getLogger ()->error ( 'The grant code from Google had no accompanying state and may be a forgery' );
throw new PostmanStateIdMissingException ();
}
$postvals = array (
'client_id' => $this->getClientId (),
'client_secret' => $this->getClientSecret (),
'grant_type' => 'authorization_code',
'redirect_uri' => $this->getCallbackUri (),
'code' => $code
);
$response = PostmanUtils::remotePostGetBodyOnly ( $this->getTokenUrl (), $postvals );
$this->processResponse ( $response );
$this->getAuthorizationToken ()->setVendorName ( self::VENDOR_NAME );
return true;
} else {
$this->getLogger ()->debug ( 'Expected code in the request header but found none - user probably denied request' );
return false;
}
}
public function getAuthorizationUrl() {
return self::GOOGLE_ENDPOINT;
}
public function getTokenUrl() {
return self::GOOGLE_REFRESH;
}
}
}
Loading

0 comments on commit edff34b

Please sign in to comment.