php/googleClientLib/cloud.php
<?php
/* ------------ google auth2: client and ResourceOwneR Authorization
see https://developers.google.com/identity/protocols/oauth2/web-server
create client/project and oauth consent screen: https://console.cloud.google.com/apis
unter Anmeldedaten (credentials) oauth clients (nach erstelle neue Anmeldedaten)
Typ=Webanwedung (auto generated?) für online und cli
nur hier bekomme ich beim editieren: Autorisierte Weiterleitungs-URIs und gebe sowohl das callback (für cli) und das online web
Typ = Desktop funktionier für cli, cannot specify redirect uri, but is not necessary for cli
und json herunterladen und als client authorisierungs config ==> $client->setAuthConfig(...)
installation for google see googledrive.php
*/
require_once 'env.php';
define("DTLTZ", new DateTimeZone(date_default_timezone_get()));
define("DTLFMT", 'Y-m-d\tH:i:s');
function toLocalTst($t) {
/* DateTime constructor uses timezone in timestring, and does not ignore it as date_create_from_format(DATE_RFC7231, $t) etc..
setTimezonew will change the time digits, so its the same moment, it even knows about summertime changes in the past years
so ->getOffset() it a method of DateTime not TimeZone!!!
*/
return is_null($t) ? null : (is_string($t) ? new DateTime($t) : $t)->setTimezone(DTLTZ)->format(DTLFMT);
}
class CloudFactory {
public $sapi, $cfgPa, $redirUri, $goAuthToken,$goAuthCode, $goAutState;
public function __construct($at=null, $cp=null, $ru=null) {
$this->sapi = $at ?? php_sapi_name() === 'cli' ? 'Cli' : 'Sess';
$this->cfgPa = $cp ?? str_starts_with($rp = realpath(__FILE__), '/wkData/www/') ? '/var/cloudKey/' # path prefix for locallly stored keys
: (str_starts_with($rp, '/home/ch45859/web/') ? substr($rp, 0, strpos($rp, '/', 18)) . 'private/cloudKey/'
: err("realpath(__FILE__) $rp not supported"));
$this->redirUri = $ru ?? $this->sapi === 'Sess' ? "$_SERVER[REQUEST_SCHEME]://$_SERVER[HTTP_HOST]$_SERVER[PHP_SELF]"
: (gethostname() === 'wk13' ? 'http://localhost/home/inf/php/goAuth2callback.php'
: err("no redirUri for host " . gethostname()));
if ($this->sapi === 'Sess')
$this->beginSess();
}
public function makenc(...$opt) {
require_once 'nextcloud.php';
return new NextCloud(...$opt);
}
public function makegodr($id, $ty, $cliN, $usr) { # make a google drive object for client/application $cliN and ResourceOwneR $rorN
# echo "makegodr($id, $ty, $cliN, $usr)\n";
$a = new ("GoAuth$this->sapi")($this, $cliN, $id);
#var_dump($a);
$gc = $a->goClient($cliN, $id);
$dr = new GoogleDrive($gc, $id, $ty, $cliN, $usr);
# var_dump($dr);
return $dr;
}
public function make($ai) {
if (is_array($ai))
return $this->{"make$ai[1]"}(...$ai);
if (! isset($this->key)) {
$this->key = [];
$hdr = fgetcsv($fk = fopen("$this->cfgPa/cloudKey.csv", 'r'));
while ($r = fgetcsv($fk))
if (count($r) > 2)
$this->key[$r[0]] = $r;
dbg(2, 'got key', $this->key);
}
$j = $this->key[$ai] ?? err("cfgKey $ai not found");
return $this->{"make$j[1]"}(...$j);
}
public function beginSess() {
if (session_status() !== PHP_SESSION_ACTIVE)
if (! session_start())
err('could not start session');
#echo "<br>\$_SESSION " . print_r($_SESSION);
#echo "<br>\$_GET " . print_r($_GET);
if (! (isset($_GET['code']) and isset($_GET['state']))) {
# echo "goauth does not seem redirect from google OAUTH2";
return;
}
if (! isset($_SESSION['goAuthOriginalState']))
err ('not set $_SESSION[goAuthOriginalState]');
if (! isset($_SESSION['goAuthOriginalGet']))
err ('not set $_SESSION[goAuthOriginalGet]');
if ($_GET['state'] !== $_SESSION['goAuthOriginalState'])
err('goautSessionStart state mismatch from google OAUTH2');
$this->goAuthCode = $_GET['code'];
$_GET = $_SESSION['goAuthOriginalGet'];
out("after swap goAuthCode $this->goAuthCode");
OUT("\$_GET after swap", $_GET);
unset($_SESSION['goAuthOriginalGet']);
unset($_SESSION['goAuthOriginalState']);
}
} # end class CloudFactory
class GoAuthCli {
public $fact;
public $scopes =
[ 'https://www.googleapis.com/auth/drive.metadata.readonly' # drive readonly
, 'https://www.googleapis.com/auth/drive' # drive file update
, 'https://www.googleapis.com/auth/documents.readonly' # docs readonly
#, 'https://www.googleapis.com/auth/documents' # docs readWrite
];
public function __construct($f) {
$this->fact = $f;
}
public function tokenGet($cliRor) { # get Authorization Token for $cliRor (client . ResourceOwneR) from a file
$pa = $this->fact->cfgPa . "{$cliRor}Token.json"; // path to resource owner tokens
if (! file_exists($pa))
return null;
$accessToken = json_decode(file_get_contents($pa), true);
dbg1("--- tokenGet from file $pa:", $accessToken);
return $accessToken;
}
public function tokenPut($cliRor, $tk) {
$pa = $this->fact->cfgPa . "{$cliRor}Token.json"; // path to resource owner tokens
if (!file_exists(dirname($pa)))
mkdir(dirname($pa), 0700, true);
file_put_contents($pa, json_encode($tk));
out("written $cliRor new resource owner token to $pa");
}
function codeGet($authUrl, $state) {
/* we request from google a code to authorize the resource owner
because we are in the cli interface, we start a web browseer, and google will send the answer to an url
our url will write the received code into a file RRCPA, that we can read
*/
$pa = $this->fact->cfgPa . "goAuthCode.csv"; // path to resource owner tokens
unlink($pa);
out("opening google authorization: xdg-open $authUrl");
#system("chromium --ozone-platform=wayland '$authUrl' &");
system("xdg-open '$authUrl' &");
out("opened google authorization: xdg-open $authUrl");
do {
sleep(2);
out("waiting for you to give google authorization in browser");
} while(! is_file($pa));
$cc = fgetcsv($f = fopen($pa, 'r'));
fclose($f);
if ($cc[0] !== $state)
err("state mismatch got $cc[0] not as expected $state");
out("found code $cc[1] scope $cc[2]");
unlink($pa);
return $cc[1];
}
function codePut() {
$pa = $this->fact->cfgPa . "goAuthCode.csv"; // path to resource owner tokens
$cd = [ $_GET['state'] ?? err("codePut state not defined", $_GET)
, $_GET['code'] ?? err("codePut code not defined", $_GET)
, $_GET['scope'] ?? err("codePut scope not defined", $_GET)];
out("codePut", $cd, ", writing to $pa");
fputcsv($f = fopen($pa, 'w'), $cd);
fclose($f);
}
public function goClient($cliN, $rorN) {
/* build and return an authorized google client
$cliN = client name, i.e. the application/project as register at google a
at Konto ...Drittanbieter-Apps und -Dienste: https://myaccount.google.com/connections
$rorN = resource owner Name, i.e.the user owning the drive
*/
require_once 'googledrive.php';
$cliPa = $this->fact->cfgPa . "goAuthCli$cliN.json"; // path to client (application) infos/keys
$cliRor = "goAuthCli{$cliN}Ror$rorN";
$client = new Google_Client();
# $client->setApplicationName($cliN); # "NQuickstart"); # seems unnecessary ....
$client->setAuthConfig($cliPa);
$client->setScopes($this->scopes);
$client->setAccessType('offline');
# $client->setDeveloperKey($apikey); # does not work: apiKey only identifies client, and prohibits any access to private data
if ($oldToken = $this->tokenGet($cliRor)) { // get previously obtained accessToken
$client->setAccessToken($oldToken);
if (! $client->isAccessTokenExpired())
return $client;
out("resource owner $cliRor access token is expired - refreshing");
// Refresh the token if possible, else fetch a new one.
if ($newToken = $client->fetchAccessTokenWithRefreshToken()) {
if ( ! isset($newToken['access_token']))
unset($newToken);
else
out("refreshed ok");
}
}
if (! isset($newToken)) {
// Request authorization from the user using google authorization.
$client->setState($state = base64_encode(random_bytes(16)));
$client->setRedirectUri($this->fact->redirUri);
#echo "aredirUri {$this->fact->redirUri}\n";
$authUrl = $client->createAuthUrl();
$code = $this->codeGet($authUrl, $state);
// Exchange authorization code for an access token.
out("got code $code");
$newToken = $client->fetchAccessTokenWithAuthCode($code);
out("got newresource owner access token", $newToken);
}
if (! isset($newToken['access_token']))
err("accesstoken invalid ", $newToken);
$this->tokenPut($cliRor, $newToken);
return $client;
}
} # end class GoAuthCli
class GoAuthSess extends GoAuthCli {
public function __construct($f) {
parent::__construct($f);
}
public function tokenGet($cliRor) { # get Authorization Token for $cliRor (client . ResourceOwneR) from a file
return $_SESSION[$cliRor] ?? null;
}
public function tokenPut($cliRor, $tk) {
$_SESSION[$cliRor] = $tk;
}
function codeGet($authUrl, $state) {
/* we request from google at $authUrl a code to authorize the resource owner
thus we will redirect to $authUrl
our redirectURI is this same script, that will detect the code and state in $_GET (in goAuthBeginWeb()) and put it to $goAuthWeb RRCPA, that we can read
$state is a random string, that will be used to check, that we do not use a missdirected code
we must save for later restore the $_GET variables
*/
if (isset($this->fact->goAuthCode)) {
out("returniong code". $this->fact->goAuthCode);
return $this->fact->goAuthCode;
}
$_SESSION['goAuthOriginalState'] = $state;
$_SESSION['goAuthOriginalGet'] = $_GET;
header("location: $authUrl"); // redirect to
exit(); // exit the script, the redirect from google will start it new
}
function codePut() {
err( __METHOD__ . ' should not be called - work is done in Cloud->beginSess');
}
} # end class GoAuthSess