Compare commits
5 Commits
0e4ab60d77
...
0.8
| Author | SHA1 | Date | |
|---|---|---|---|
| 255d0189ab | |||
| cbc0b936f8 | |||
| f1e1c8fd18 | |||
| 8b1a5ca4a2 | |||
| 3c88ccd4cc |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,7 @@
|
||||
vendor
|
||||
build
|
||||
cache
|
||||
.cache
|
||||
.config
|
||||
.local
|
||||
*.phar
|
||||
@@ -9,7 +9,8 @@ namespace CloudObjects\SDK\AccountGateway;
|
||||
use ML\IRI\IRI;
|
||||
use ML\JsonLD\Document, ML\JsonLD\JsonLD, ML\JsonLD\Node;
|
||||
use Symfony\Component\HttpFoundation\Request, Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
|
||||
use Nyholm\Psr7\Factory\Psr17Factory;
|
||||
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
|
||||
use GuzzleHttp\Client, GuzzleHttp\HandlerStack, GuzzleHttp\Middleware;
|
||||
use Psr\Http\Message\RequestInterface, Psr\Http\Message\ResponseInterface;
|
||||
|
||||
@@ -110,8 +111,11 @@ class AccountContext {
|
||||
new IRI('aauid:'.$request->headers->get('C-AAUID')),
|
||||
$request->headers->get('C-Access-Token'));
|
||||
|
||||
$psr7Factory = new DiactorosFactory;
|
||||
$context->parsePsrRequest($psr7Factory->createRequest($request));
|
||||
// Convert HTTP Foundation to PSR17
|
||||
// based on: https://symfony.com/doc/current/components/psr7.html#converting-from-httpfoundation-objects-to-psr-7
|
||||
$psr17Factory = new Psr17Factory;
|
||||
$psrHttpFactory = new PsrHttpFactory($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory);
|
||||
$context->parsePsrRequest($psrHttpFactory->createRequest($request));
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
@@ -154,21 +154,48 @@ class APIClientFactory {
|
||||
'timeout' => self::DEFAULT_TIMEOUT
|
||||
];
|
||||
|
||||
if ($this->reader->hasPropertyValue($api, 'wa:supportsAuthenticationMechanism',
|
||||
'wa:APIKeyAuthentication'))
|
||||
if ($this->reader->hasProperty($api, 'wa:hasAuthorizationServer')) {
|
||||
// We have an authorization server for this endpoint/API
|
||||
$authServerCoid = $this->reader->getFirstValueIRI($api, 'wa:hasAuthorizationServer');
|
||||
$authServerObject = $this->objectRetriever->getObject($authServerCoid);
|
||||
if (!isset($authServer))
|
||||
throw new InvalidObjectConfigurationException("Authorization server object <"
|
||||
. (string)$authServerCoid . "> not available.");
|
||||
|
||||
try {
|
||||
$authServer = new OAuth2AuthServer($authServerObject);
|
||||
} catch (Exception $e) {
|
||||
throw new InvalidObjectConfigurationException("Authorization server object <"
|
||||
. (string)$authServerCoid . "> could not be loaded. Its definition may be invalid.");
|
||||
}
|
||||
|
||||
try {
|
||||
$authServer->configureConsumer($this->namespace);
|
||||
} catch (Exception $e) {
|
||||
throw new InvalidObjectConfigurationException("The namespace <" . $this->namespace->getId()
|
||||
. "> does not contain valid configuration to use the authorization server <"
|
||||
. (string)$authServerCoid . ">.");
|
||||
}
|
||||
|
||||
// Get access token through the auth server
|
||||
$clientConfig['headers']['Authorization'] = 'Bearer ' . $authServer->getAccessToken();
|
||||
} elseif ($this->reader->hasPropertyValue($api, 'wa:supportsAuthenticationMechanism',
|
||||
'wa:APIKeyAuthentication')) {
|
||||
// API key authentication
|
||||
$clientConfig = $this->configureAPIKeyAuthentication($api, $clientConfig);
|
||||
|
||||
elseif ($this->reader->hasPropertyValue($api, 'wa:supportsAuthenticationMechanism',
|
||||
'oauth2:FixedBearerTokenAuthentication'))
|
||||
} elseif ($this->reader->hasPropertyValue($api, 'wa:supportsAuthenticationMechanism',
|
||||
'oauth2:FixedBearerTokenAuthentication')) {
|
||||
// Fixed bearer token authentication
|
||||
$clientConfig = $this->configureBearerTokenAuthentication($api, $clientConfig);
|
||||
|
||||
elseif ($this->reader->hasPropertyValue($api, 'wa:supportsAuthenticationMechanism',
|
||||
'wa:HTTPBasicAuthentication'))
|
||||
} elseif ($this->reader->hasPropertyValue($api, 'wa:supportsAuthenticationMechanism',
|
||||
'wa:HTTPBasicAuthentication')) {
|
||||
// HTTP Basic authentication
|
||||
$clientConfig = $this->configureBasicAuthentication($api, $clientConfig);
|
||||
|
||||
elseif ($this->reader->hasPropertyValue($api, 'wa:supportsAuthenticationMechanism',
|
||||
'wa:SharedSecretAuthenticationViaHTTPBasic'))
|
||||
} elseif ($this->reader->hasPropertyValue($api, 'wa:supportsAuthenticationMechanism',
|
||||
'wa:SharedSecretAuthenticationViaHTTPBasic')) {
|
||||
// HTTP Basic authentication using shared secrets in CloudObjects Core
|
||||
$clientConfig = $this->configureSharedSecretBasicAuthentication($api, $clientConfig);
|
||||
}
|
||||
|
||||
if ($specificClient == false)
|
||||
return new Client($clientConfig);
|
||||
|
||||
14
CloudObjects/SDK/WebAPI/Exceptions/OAuthFlowException.php
Normal file
14
CloudObjects/SDK/WebAPI/Exceptions/OAuthFlowException.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
namespace CloudObjects\SDK\WebAPI\Exceptions;
|
||||
|
||||
/**
|
||||
* An Exception that is thrown when an an OAuth flow failed.
|
||||
*/
|
||||
class OAuthFlowException extends \Exception {
|
||||
|
||||
}
|
||||
105
CloudObjects/SDK/WebAPI/OAuth2AuthServer.php
Normal file
105
CloudObjects/SDK/WebAPI/OAuth2AuthServer.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
namespace CloudObjects\SDK\WebAPI;
|
||||
|
||||
use Exception;
|
||||
use ML\JsonLD\Node;
|
||||
use GuzzleHttp\Client;
|
||||
use Webmozart\Assert\Assert;
|
||||
use CloudObjects\SDK\NodeReader;
|
||||
|
||||
class OAuth2AuthServer {
|
||||
|
||||
private $reader;
|
||||
private $authServer;
|
||||
private $consumer;
|
||||
|
||||
private $grantType;
|
||||
private $clientId;
|
||||
private $clientSecret;
|
||||
|
||||
public function __construct(Node $authServer) {
|
||||
$this->reader = new NodeReader([
|
||||
'prefixes' => [
|
||||
'oauth2' => 'coid://oauth2.co-n.net/'
|
||||
]
|
||||
]);
|
||||
|
||||
Assert::true($this->reader->hasProperty($authServer, 'oauth2:hasTokenEndpoint'),
|
||||
"Authorization Server must have a token endpoint.");
|
||||
Assert::startsWith($this->reader->getFirstValueString($authServer, 'oauth2:hasTokenEndpoint'),
|
||||
"https://",
|
||||
"Token endpoint must be an https:// URL.");
|
||||
Assert::true($this->reader->hasProperty($authServer, 'oauth2:supportsGrantType'),
|
||||
"Authorization Server must support at least one grant type.");
|
||||
Assert::true($this->reader->hasProperty($this->authServer, 'oauth2:usesClientIDFrom'),
|
||||
"Authorization Server must define client ID property.");
|
||||
Assert::true($this->reader->hasProperty($this->authServer, 'oauth2:usesClientSecretFrom'),
|
||||
"Authorization Server must define client secret property.");
|
||||
|
||||
$this->authServer = $authServer;
|
||||
}
|
||||
|
||||
private function assertClientCredentialPropertiesExist() : void {
|
||||
|
||||
}
|
||||
|
||||
public function configureConsumer(Node $consumer) : void {
|
||||
$this->assertClientCredentialPropertiesExist();
|
||||
$clientIDProperty = $this->reader->getFirstValueString($this->authServer,
|
||||
'oauth2:usesClientIDFrom');
|
||||
$clientSecretProperty = $this->reader->getFirstValueString($this->authServer,
|
||||
'oauth2:usesClientSecretFrom');
|
||||
|
||||
Assert::true($this->reader->hasProperty($consumer, $clientIDProperty),
|
||||
"Namespace must have Client ID");
|
||||
Assert::true($this->reader->hasProperty($consumer, $clientSecretProperty),
|
||||
"Namespace must have Client Secret");
|
||||
|
||||
if ($this->reader->hasPropertyValue($this->authServer,
|
||||
'oauth2:supportsGrantType', 'oauth2:ClientCredentials')) {
|
||||
// No additional conditions for "client_credentials" flow
|
||||
$this->grantType = 'client_credentials';
|
||||
} else {
|
||||
throw new Exception("No flow/grant_type found.");
|
||||
}
|
||||
|
||||
$this->consumer = $consumer;
|
||||
$this->clientId = $this->reader->getFirstValueString($consumer, $clientIDProperty);
|
||||
$this->clientSecret = $this->reader->getFirstValueString($consumer, $clientSecretProperty);
|
||||
}
|
||||
|
||||
public function getAccessToken() {
|
||||
Assert::notNull($this->consumer, "Missing consumer.");
|
||||
Assert::notNull($this->grantType, "Missing grant_type.");
|
||||
Assert::notNull($this->clientId, "Missing client_id.");
|
||||
Assert::notNull($this->clientSecret, "Missing client_secret.");
|
||||
|
||||
$client = new Client;
|
||||
$tokenEndpointUrl = $this->reader->getFirstValueString($this->authServer, 'oauth2:hasTokenEndpoint');
|
||||
$params = [
|
||||
'grant_type' => $this->grantType,
|
||||
'client_id' => $this->clientId,
|
||||
'client_secret' => $this->clientSecret
|
||||
];
|
||||
|
||||
switch ($this->grantType) {
|
||||
case "client_credentials":
|
||||
// no additional params needed
|
||||
default:
|
||||
throw new Exception("No flow/grant_type found.");
|
||||
}
|
||||
|
||||
$tokenResponse = json_decode($client->post($tokenEndpointUrl, [
|
||||
'form_params' => $params
|
||||
])->getBody(true));
|
||||
|
||||
Assert::keyExists($tokenResponse, 'access_token');
|
||||
|
||||
return $tokenResponse['access_token'];
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
[](https://packagist.org/packages/cloudobjects/sdk) [](https://packagist.org/packages/cloudobjects/sdk)
|
||||
|
||||
[](https://app.buddy.works/cloudobjects/php-sdk/repository/branch/main)
|
||||
|
||||
The CloudObjects PHP SDK provides simple access to [CloudObjects](https://cloudobjects.io/) from PHP-based applications. It wraps the [Object API](https://coid.link/cloudobjects.io/ObjectAPI/1.0) to fetch objects from the CloudObjects Core database and provides object-based access to their RDF description. A two-tiered caching mechanism (in-memory and Doctrine cache drivers) is included. The SDK also contains a helper class to validate COIDs.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"doctrine/common" : ">=2.6.1",
|
||||
"doctrine/cache" : "1.*",
|
||||
"guzzlehttp/guzzle" : ">=6.0",
|
||||
"psr/log": "^1.1",
|
||||
"psr/log": ">=1.1",
|
||||
"kevinrob/guzzle-cache-middleware": "^3.2",
|
||||
"webmozart/assert": "^1.6"
|
||||
},
|
||||
@@ -27,13 +27,13 @@
|
||||
"phpunit/phpunit": ">=4.8.0,<5.0",
|
||||
"symfony/http-foundation" : ">=4.0",
|
||||
"symfony/psr-http-message-bridge" : ">=1.1.0",
|
||||
"zendframework/zend-diactoros" : "~1.8.6",
|
||||
"nyholm/psr7" : "~1.5.1",
|
||||
"defuse/php-encryption" : "^2.2"
|
||||
},
|
||||
"suggest" : {
|
||||
"symfony/http-foundation" : "Required to use parseSymfonyRequest() in AccountContext.",
|
||||
"symfony/psr-http-message-bridge" : "Required to use parseSymfonyRequest() in AccountContext.",
|
||||
"zendframework/zend-diactoros" : "Required to use parseSymfonyRequest() in AccountContext.",
|
||||
"nyholm/psr7" : "Required to use parseSymfonyRequest() in AccountContext.",
|
||||
"defuse/php-encryption": "Required to use CryptoHelper"
|
||||
}
|
||||
}
|
||||
|
||||
1221
composer.lock
generated
1221
composer.lock
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user