The drupal/simple_oauth module creates an oauth2_token table. This table has a column value and I've examined the source code but I cannot work out how this value is generated.
To make this a little bit more complicated, I don't have this service running locally, and I don't have access to the remote server it is running on, I only have access to the database itself.
I am creating a Python service that is using the Drupal database (because reasons). I am able to generate all the required data except this value field.
I can copy the value field from one of the existing access tokens, and use it in a new record I create via a SQL INSERT. The JWT I create from this record's value works correctly with the protected endpoint. However, if I set an arbitrary value like a UUID, the request to the protected resource fails with the following error response:
{
"error": "permission_denied",
"error_description": "The 'debug simple_oauth tokens' permission is required.",
"status_code": 403,
"detail": "permission_denied"
}
The value is used in the jti of the JWT and I can see in src/Entities/AccessTokenEntity.php that this is set to $this->getIdentifier(); (<-- comments are mine):
public function convertToJWT() {
$private_claims = [];
\Drupal::moduleHandler()
->alter('simple_oauth_private_claims', $private_claims, $this);
if (!is_array($private_claims)) {
$message = 'An implementation of hook_simple_oauth_private_claims_alter ';
$message .= 'returns an invalid $private_claims value. $private_claims ';
$message .= 'must be an array.';
throw new \InvalidArgumentException($message);
}
$id = $this->getIdentifier(); /* <-- NOTE */
$now = new \DateTimeImmutable('#' . \Drupal::time()->getCurrentTime());
$key_path = $this->privateKey->getKeyPath();
$key = InMemory::file($key_path);
$config = Configuration::forSymmetricSigner(new Sha256(), $key);
$builder = $config->builder()
->permittedFor($this->getClient()->getIdentifier())
->identifiedBy($id) /* <-- SET THE JTI */
->withHeader('jti', $id)
->issuedAt($now)
->canOnlyBeUsedAfter($now)
->expiresAt($this->getExpiryDateTime())
->relatedTo($this->getUserIdentifier())
->withClaim('scope', $this->getScopes());
And this is where my investigation has stopped, as I cannot work out where getIdentifier() is implemented.
I cross-posted this question to Drupal Answers and #apaderno noted this wasn't a Drupal specific question, which nudged me towards the source for The source for PHP OAuth 2.0 Server.
The source of the value column is the generateUniqueIdentifier function in AbstractGrant.php:
/**
* Generate a new unique identifier.
*
* #param int $length
*
* #throws OAuthServerException
*
* #return string
*/
protected function generateUniqueIdentifier($length = 40)
{
try {
return \bin2hex(\random_bytes($length));
// #codeCoverageIgnoreStart
} catch (TypeError $e) {
throw OAuthServerException::serverError('An unexpected error has occurred', $e);
} catch (Error $e) {
throw OAuthServerException::serverError('An unexpected error has occurred', $e);
} catch (Exception $e) {
// If you get this message, the CSPRNG failed hard.
throw OAuthServerException::serverError('Could not generate a random string', $e);
}
// #codeCoverageIgnoreEnd
}
Related
I need to write to log some data when exception occurred, since it is dynamic data and I need the message I tried with simple code, just to write 'Boom' if exception occurred.
However just the automatic exception data written in logs (as before the try catch).
can someone advise how to print to log in the catch?
I just need to add additional text to the exception, just to be more specific
try {
$sheet->setCellValue($cell,$innervalue);
} catch(Exception $e) {
$message = 'Can not set value: '.$innervalue .' in cell ' .$cell .$headerArray[$headerIndex];
\Log::info('boom');
}
and in the logs nothing displayed
php artisan make:Exception SomeAweSomeException
This way you have your own controle of how the exception should look and handle. Also you can use standards wich makes you code look cleaner.
class SomeCustomException extends Exception
{
// ...
/**
* Get the exception's context information.
*
* #return array
*/
public function context()
{
return ['order_id' => $this->orderId];
}
}
use Exception;
class SetDeliverDateToFarAwayException extends Exception
{
public function __construct()
{
parent::__construct(
'Selecting more than three days into the future is not allowed'
);
}
}
and now my method wich creates the error message.
/**
* Finalizes the scheduler
*
* #return string
* #throws SetDeliverDateToFarAwayException|Throwable
*/
public function set(): string
{
$today = Carbon::now();
$deliver = new Carbon();
$deliver->year($this->year ?? $today->year);
$deliver->month($this->month ?? $today->month);
$deliver->day($this->day ?? $today->day);
$deliver->hour($this->hour ?? $today->hour);
$deliver->minute($this->minute ?? $today->minute);
#Mail gun can max schedule mail for 3 days in the future. So we throw an Exception if the date is to big.
throw_if($deliver->diffInHours($today) > 72, new SetDeliverDateToFarAwayException());
return Mail::schedule($deliver->toDateTimeString());
}
this what worked for me
try {
$sheet->setCellValueExplicit('WELL DONE');
}
catch (\Exception $e) {
$errorMsg = 'the value of:\' ' .$innervalue . '\' in cell ' . $cell . ' in header: ' . $headerArray[$headerIndex] . 'problematic' ;
\Log::error($errorMsg);
\Log::error($e->getMessage());
\Log::error($e->getTraceAsString());
throw new Exception($e->getMessage());
}
To get the exception log message use it like this -
try {
} catch(Exception $e) {
Log::info(json_encode($e->getMessage()));
}
I'm following a tutorial on how to use the Google Reseller API. I've come to the section on determining whether a customer already exists in Google Apps (Step 2) but have come unstuck in handling the Google_Service_Exception object.
If a customer doesn't exist then a call to the API will return a 404 error. I'm using the code property of the Google_Service_Exception object $e to determine if the response has a 404 error. However when I try to return the error code with $e->code with:
try {
// Call to the Google Reseller API
} catch (Google_Service_Exception $e) {
if($e->code == 404){
return false;
}
}
I get the following PHP error:
Fatal error: Cannot access protected property Google_Service_Exception::$code.
The Google_Service_Exception class is as follows:
<?php
require_once 'Google/Exception.php';
class Google_Service_Exception extends Google_Exception
{
/**
* Optional list of errors returned in a JSON body of an HTTP error response.
*/
protected $errors = array();
/**
* Override default constructor to add ability to set $errors.
*
* #param string $message
* #param int $code
* #param Exception|null $previous
* #param [{string, string}] errors List of errors returned in an HTTP
* response. Defaults to [].
*/
public function __construct(
$message,
$code = 0,
Exception $previous = null,
$errors = array()
) {
if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
parent::__construct($message, $code, $previous);
} else {
parent::__construct($message, $code);
}
$this->errors = $errors;
}
/**
* An example of the possible errors returned.
*
* {
* "domain": "global",
* "reason": "authError",
* "message": "Invalid Credentials",
* "locationType": "header",
* "location": "Authorization",
* }
*
* #return [{string, string}] List of errors return in an HTTP response or [].
*/
public function getErrors()
{
return $this->errors;
}
}
So I assumed the error has something to do with the fact that $errors is protected. I imagine it is protected for a reason so I was a bit wary of changing the class. Any help / pointers in working around this error would be greatly appreciated. Thanks
Just use the getCode() method:
try {
// Call to the Google Reseller API
} catch (Google_Service_Exception $e) {
if($e->getCode() == 404){ // <- Change is here
return false;
}
}
Google_Service_Exception extends Google_Exception and Google_Exception extends Exception. You may read the documentation about Exception here. You will see the getCode method.
I getting this is exception in my application, and don't understand why. This exception throw in for one collection, over collection not throw this exception, with processed insert(),update(), or delete()
/**
* insert to mongo db method
* #param $dataArray
* #param $collection
*/
static function insert($dataArray, $collection) {
$connect = Core_Model_Mongo::getConnect();
$write = new MongoDB\Driver\BulkWrite();
$writeConcern = new MongoDB\Driver\WriteConcern(MongoDB\Driver\WriteConcern::MAJORITY);
$write->insert($dataArray);
$connect->executeBulkWrite(
Config_Db::getConf()['mongodb']['db'].'.'.$collection,
$write,
$writeConcern
);
}
end get connect method
/**
* #return \MongoDB\Driver\Manager
*/
static function getConnect() {
if(!is_null(self::$_connect)) {
return self::$_connect;
}
self::$_connect = new \MongoDB\Driver\Manager(Config_Db::getConf()['mongodb']['connect']);
return self::$_connect;
}
data save in collection success full, and this exception not take insert(),update() and other methods. I temporary resolve this question by try catch block
try {
// my code
} catch(MongoDB\Driver\Exception\BulkWriteException $error) {
Core_App::log(var_export($error,true));
}
but this is resolve not correct because I need application without any exception,error,notice, and over log level error.
Should I check if a key exists and then get it or just get it (when I need to get it, not check if its set)?
What is more reliable? safer? faster?
Examples:
1) PHP redis (https://github.com/nicolasff/phpredis)
if ($redis->exists('key'))
echo $redis->get('key');
// VS
if ($value = $redis->get('key'))
echo $value;
2) PHP phalcon cookies(http://docs.phalconphp.com/pt/latest/reference/cookies.html)
if ($this->cookies->has('remember-me'))
echo $this->cookies->get('remember-me')->getValue()
// VS
if ($value = $this->cookies->get('remember-me')->getValue())
echo $value;
Thank you!
My interpretation of this problem is:
I don't like writing things like
if ($value = $redis->get('key'))
echo $value;
it makes code unclear.
Also, why is checking if variable exists so important? Because it simplifies control flow.
Let's consider that you're grabbing some data from service to render it on page. You can write low quality code with multiple ifs, but you can also try something like this:
offerServiceImpl.php
class offerServiceImpl implements offerService {
//... (some methods)
/**
* #param int $offerId
* #return Offer
* #throws InvalidArgumentException
* #throws RuntimeException
*/
public function getOffer($offerId)
{
if (!$offerId || !is_numeric($offerId)) {
throw new InvalidArgumentException("Invalid offer id: " . $offerId);
}
$offer = $this->offerDao->get($offerId);
if (!$offer) {
//could be your own exception class
throw new RuntimeException("Could not found offer " . $offerId);
} else {
return $offer;
}
}
}
offersControler.php
class offersController extends AbstractController{
public function index($id){
//... some code
try{
$offer = $this->offerService->getOffer($id);
} catch (InvalidArgumentException $ex) {
//log error, perform redirect to error 500
} catch (RuntimeException $ex){
//log another error, perform redirect to error 404
} catch (Exception $ex){
//log error, perform redirect to error 500
}
}
}
I have php code that execute python cgi and I want to pass python trace (returned from cgi) as extra data to php exception how can I do this and how can I get that value from catch(Exception e) { (It should check if that extra value exesit or not).
I have code like this:
$response = json_decode(curl_exec($ch));
if (isset($response->error)) {
// how to send $response->trace with exception.
throw new Exception($response->error);
}
return $response->result;
and I use json-rpc library that should return that data to the user:
} catch (Exception $e) {
//catch all exeption from user code
$msg = $e->getMessage();
echo response(null, $id, array("code"=>200, "message"=>$msg));
}
Do I need to write new type of exception or can I do this with normal Exception? I would like to send everything that was thrown in "data" =>
You need to extend Exception class:
<?php
class ResponseException extends Exception
{
private $_data = '';
public function __construct($message, $data)
{
$this->_data = $data;
parent::__construct($message);
}
public function getData()
{
return $this->_data;
}
}
When throwing:
<?php
...
throw new ResponseException($response->error, $someData);
...
And when catching:
catch(ResponseException $e) {
...
$data = $e->getData();
...
}
Dynamic Property (not recommended)
Please note that this will cause deprecation error in PHP 8.2 and will stop working in PHP 9 according to one of the PHP RFC https://wiki.php.net/rfc/deprecate_dynamic_properties
As the OP asking about doing this task without extending Exception class, you can totally skip ResponseException class declaration. I really not recommend do it this way, unless you've got really strong reason (see this topic for more details: https://softwareengineering.stackexchange.com/questions/186439/is-declaring-fields-on-classes-actually-harmful-in-php)
In throwing section:
...
$e = new Exception('Exception message');
$e->data = $customData; // we're creating object property on the fly
throw $e;
...
and when catching:
catch(Exception $e) {
$data = $e->data; // Access data property
}
September 2018 edit:
As some of readers found this answer useful, I have added a link to another Stack Overflow question which explains the downsides of using dynamically declared properties.
Currently, your code converts the response text directly into an object without any intermediate step. Instead, you could always just keep the serialized (via JSON) text it and append it to the end of the Exception message.
$responseText = curl_exec($ch);
$response = json_decode($responseText);
if (isset($response->error)) {
throw new Exception('Error when fetching resource. Response:'.$responseText);
}
return $response->result;
Then you could just recover everything after "Response:" in your error log and optionally de-serialize it or just read it.
As an aside, I would also not count on the server sending JSON, you should verify that the response text was actually parseable as JSON and return a separate error for that if it isn't.