shell bypass 403
<?php namespace Akaunting\Setting\Drivers; use Akaunting\Setting\Contracts\Driver; use Akaunting\Setting\Support\Arr; use Closure; use Illuminate\Database\Connection; use Illuminate\Support\Arr as LaravelArr; use Illuminate\Support\Facades\Crypt; class Database extends Driver { /** * The database connection instance. * * @var \Illuminate\Database\Connection */ protected $connection; /** * The table to query from. * * @var string */ protected $table; /** * The key column name to query from. * * @var string */ protected $key; /** * The value column name to query from. * * @var string */ protected $value; /** * Keys which should be encrypt automatically. * * @var string */ protected $encrypted_keys; /** * Any query constraints that should be applied. * * @var Closure|null */ protected $query_constraint; /** * Any extra columns that should be added to the rows. * * @var array */ protected $extra_columns = []; /** * @param \Illuminate\Database\Connection $connection * @param string $table */ public function __construct(Connection $connection, $table = null, $key = null, $value = null, $encrypted_keys = []) { $this->connection = $connection; $this->table = $table ?: 'settings'; $this->key = $key ?: 'key'; $this->value = $value ?: 'value'; $this->encrypted_keys = $encrypted_keys ?: []; } /** * Set the table to query from. * * @param string $table */ public function setTable($table) { $this->table = $table; } /** * Set the key column name to query from. * * @param string $key */ public function setKey($key) { $this->key = $key; } /** * Set the value column name to query from. * * @param string $value */ public function setValue($value) { $this->value = $value; } /** * Set the query constraint. * * @param Closure $callback */ public function setConstraint(Closure $callback) { $this->data = []; $this->loaded = false; $this->query_constraint = $callback; } /** * Set extra columns to be added to the rows. * * @param array $columns */ public function setExtraColumns(array $columns) { $this->extra_columns = $columns; } /** * Get extra columns added to the rows. * * @return array */ public function getExtraColumns() { return $this->extra_columns; } /** * {@inheritdoc} */ public function forget($key) { parent::forget($key); // because the database driver cannot store empty arrays, remove empty // arrays to keep data consistent before and after saving $segments = explode('.', $key); array_pop($segments); while ($segments) { $segment = implode('.', $segments); // non-empty array - exit out of the loop if ($this->get($segment)) { break; } // remove the empty array and move on to the next segment $this->forget($segment); array_pop($segments); } } /** * {@inheritdoc} */ protected function write(array $data) { // Get current data $db_data = $this->newQuery()->get([$this->key, $this->value])->toArray(); $insert_data = LaravelArr::dot($data); $update_data = []; $delete_keys = []; foreach ($db_data as $db_row) { $key = $db_row->{$this->key}; $value = $db_row->{$this->value}; $is_in_insert = $is_different_in_db = $is_same_as_fallback = false; if (isset($insert_data[$key])) { $is_in_insert = true; $is_different_in_db = (string) $insert_data[$key] != (string) $value; $is_same_as_fallback = $this->isEqualToFallback($key, $insert_data[$key]); } if ($is_in_insert) { if ($is_same_as_fallback) { // Delete if new data is same as fallback $delete_keys[] = $key; } elseif ($is_different_in_db) { // Update if new data is different from db $update_data[$key] = $insert_data[$key]; } } else { // Delete if current db not available in new data $delete_keys[] = $key; } unset($insert_data[$key]); } foreach ($update_data as $key => $value) { $value = $this->prepareValue($key, $value); $this->newQuery() ->where($this->key, '=', $key) ->update([$this->value => $value]); } if ($insert_data) { $this->newQuery(true) ->insert($this->prepareInsertData($insert_data)); } if ($delete_keys) { $this->newQuery() ->whereIn($this->key, $delete_keys) ->delete(); } } /** * Transforms settings data into an array ready to be insterted into the * database. Call array_dot on a multidimensional array before passing it * into this method! * * @param array $data Call array_dot on a multidimensional array before passing it into this method! * * @return array */ protected function prepareInsertData(array $data) { $db_data = []; if ($this->getExtraColumns()) { foreach ($data as $key => $value) { $value = $this->prepareValue($key, $value); // Don't insert if same as fallback if ($this->isEqualToFallback($key, $value)) { continue; } $db_data[] = array_merge( $this->getExtraColumns(), [$this->key => $key, $this->value => $value] ); } } else { foreach ($data as $key => $value) { $value = $this->prepareValue($key, $value); // Don't insert if same as fallback if ($this->isEqualToFallback($key, $value)) { continue; } $db_data[] = [$this->key => $key, $this->value => $value]; } } return $db_data; } /** * Checks if the provided key should be encrypted or not. * Also type casts the given value to a string so errors with booleans or integers are handeled. * Otherwise it returns the original value. * * @param string $key Key to check if it's inside the encryptedValues variable. * @param mixed $value Info: Encryption only supports strings. * * @return string */ protected function prepareValue(string $key, $value) { // Check if key should be encrypted if (in_array($key, $this->encrypted_keys)) { // Cast to string to avoid error when a user passes a boolean value return Crypt::encryptString((string) $value); } return $value; } /** * Checks if the provided key should be decrypted or not. * Otherwise it returns the original value. * * @param string $key Key to check if it's inside the encryptedValues variable. * @param mixed $value Info: Encryption only supports strings. * * @return string */ protected function unpackValue(string $key, $value) { // Check if key should be encrypted if (in_array($key, $this->encrypted_keys)) { // Cast to string to avoid error when a user passes a boolean value return Crypt::decryptString((string) $value); } return $value; } /** * {@inheritdoc} */ protected function read() { return $this->parseReadData($this->newQuery()->get()); } /** * Parse data coming from the database. * * @param array $data * * @return array */ public function parseReadData($data) { $results = []; foreach ($data as $row) { if (is_array($row)) { $key = $row[$this->key]; $value = $row[$this->value]; } elseif (is_object($row)) { $key = $row->{$this->key}; $value = $row->{$this->value}; } else { $msg = 'Expected array or object, got ' . gettype($row); throw new \UnexpectedValueException($msg); } // Encryption $value = $this->unpackValue($key, $value); Arr::set($results, $key, $value); } return $results; } /** * Create a new query builder instance. * * @param bool $insert * * @return \Illuminate\Database\Query\Builder */ protected function newQuery($insert = false) { $query = $this->connection->table($this->table); if (!$insert) { foreach ($this->getExtraColumns() as $key => $value) { $query->where($key, '=', $value); } } if ($this->query_constraint !== null) { $callback = $this->query_constraint; $callback($query, $insert); } return $query; } }