feat: sync, webhook, tokens refresh, fillable, key config
This commit is contained in:
parent
4156db2cdc
commit
830ebbcdd3
|
@ -19,13 +19,13 @@ class RefreshTokens extends Command
|
||||||
|
|
||||||
$has_fields = in_array(HasConnectTokens::class, class_uses($class));
|
$has_fields = in_array(HasConnectTokens::class, class_uses($class));
|
||||||
|
|
||||||
if (!$has_fields) {
|
if (! $has_fields) {
|
||||||
throw new ConnectException("$class does not implement HasConnectTokens");
|
throw new ConnectException("$class does not implement HasConnectTokens");
|
||||||
}
|
}
|
||||||
|
|
||||||
$class::query()->chunks(10, function ($models) use ($connect) {
|
$class::query()->chunks(10, function ($models) use ($connect) {
|
||||||
$models->each(function ($model) use ($connect) {
|
$models->each(function ($model) use ($connect) {
|
||||||
if (!empty($model->connect_refresh_token) && $model->connect_expires_at <= now()->addHour()) {
|
if (! empty($model->connect_refresh_token) && $model->connect_expires_at <= now()->addHour()) {
|
||||||
try {
|
try {
|
||||||
$tokens = $connect->getAccessTokenFromRefreshToken($model->connect_refresh_token);
|
$tokens = $connect->getAccessTokenFromRefreshToken($model->connect_refresh_token);
|
||||||
$connect->updateUserConnectData($model, $tokens);
|
$connect->updateUserConnectData($model, $tokens);
|
||||||
|
|
|
@ -21,14 +21,14 @@ class Sync extends Command
|
||||||
|
|
||||||
$has_fields = in_array(HasConnectTokens::class, class_uses($class));
|
$has_fields = in_array(HasConnectTokens::class, class_uses($class));
|
||||||
|
|
||||||
if (!$has_fields) {
|
if (! $has_fields) {
|
||||||
throw new ConnectException("$class does not implement HasConnectTokens");
|
throw new ConnectException("$class does not implement HasConnectTokens");
|
||||||
}
|
}
|
||||||
|
|
||||||
$class::query()->chunks(10, function ($models) use ($connect) {
|
$class::query()->chunks(10, function ($models) use ($connect) {
|
||||||
$models->each(function ($model) use ($connect) {
|
$models->each(function ($model) use ($connect) {
|
||||||
try {
|
try {
|
||||||
if (!empty($model->connect_access_token)) {
|
if (! empty($model->connect_access_token)) {
|
||||||
$data = $connect->getUserData($model->connect_access_token);
|
$data = $connect->getUserData($model->connect_access_token);
|
||||||
$connect->updateUserData($model, $data);
|
$connect->updateUserData($model, $data);
|
||||||
$model->save();
|
$model->save();
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
namespace Bluesquare\Connect;
|
namespace Bluesquare\Connect;
|
||||||
|
|
||||||
use Bluesquare\Connect\Traits\HasConnectData;
|
use Bluesquare\Connect\Traits\HasConnectData;
|
||||||
use Bluesquare\Connect\Traits\HasConnectSync;
|
|
||||||
use Bluesquare\Connect\Traits\HasConnectTokens;
|
use Bluesquare\Connect\Traits\HasConnectTokens;
|
||||||
|
use Bluesquare\Connect\Traits\HasConnectWebhook;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Routing\Router;
|
use Illuminate\Routing\Router;
|
||||||
|
@ -44,11 +44,11 @@ class Connect
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($access_token !== null) {
|
if (! is_null($access_token)) {
|
||||||
$config['headers']['Authorization'] = 'Bearer ' . $access_token;
|
$config['headers']['Authorization'] = 'Bearer ' . $access_token;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_null($data)) {
|
if (! is_null($data)) {
|
||||||
$config['form_params'] = $data;
|
$config['form_params'] = $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ class Connect
|
||||||
|
|
||||||
$states = session()->get('connect_states');
|
$states = session()->get('connect_states');
|
||||||
|
|
||||||
if (!is_array($states))
|
if (! is_array($states))
|
||||||
$states = [];
|
$states = [];
|
||||||
|
|
||||||
$states[] = $state;
|
$states[] = $state;
|
||||||
|
@ -99,15 +99,15 @@ class Connect
|
||||||
|
|
||||||
public function checkState(Request $request)
|
public function checkState(Request $request)
|
||||||
{
|
{
|
||||||
if (!session()->has('connect_states'))
|
if (! session()->has('connect_states'))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
$states = session()->get('connect_states');
|
$states = session()->get('connect_states');
|
||||||
|
|
||||||
if (!is_array($states))
|
if (! is_array($states))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!$request->has('state') || !in_array($request->state, $states))
|
if (! $request->has('state') || ! in_array($request->state, $states))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
unset($states[array_search($request->state, $states)]);
|
unset($states[array_search($request->state, $states)]);
|
||||||
|
@ -119,22 +119,21 @@ class Connect
|
||||||
|
|
||||||
public function loginFromCallback(Request $request, $redirect_to = '/')
|
public function loginFromCallback(Request $request, $redirect_to = '/')
|
||||||
{
|
{
|
||||||
if (!$this->checkState($request) || !$request->has('code'))
|
if (! $this->checkState($request) || ! $request->has('code'))
|
||||||
return redirect('/');
|
return redirect('/');
|
||||||
|
|
||||||
// Access token
|
// Access token
|
||||||
|
|
||||||
$expires_at = now();
|
$expires_at = now();
|
||||||
$connect_data = $this->getAccessTokenFromAuthorizationCode($request->code);
|
$connect_data = $this->getAccessTokenFromAuthorizationCode($request->get('code'));
|
||||||
$connect_data['expires_at'] = $expires_at->addSeconds($connect_data['expires_in']);
|
$connect_data['expires_at'] = $expires_at->addSeconds($connect_data['expires_in']);
|
||||||
|
|
||||||
// User data
|
// User data
|
||||||
|
|
||||||
$user_data = $this->getUserData($connect_data['access_token']);
|
$user_data = $this->getUserData($connect_data['access_token']);
|
||||||
$user = $this->resolveUser($user_data);
|
|
||||||
|
|
||||||
|
$user = $this->sync('create', $user_data);
|
||||||
$this->updateUserConnectData($user, $connect_data);
|
$this->updateUserConnectData($user, $connect_data);
|
||||||
$this->updateUserData($user, $user_data);
|
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
// Login
|
// Login
|
||||||
|
@ -148,7 +147,7 @@ class Connect
|
||||||
|
|
||||||
public function getAccessTokenFromAuthorizationCode($code)
|
public function getAccessTokenFromAuthorizationCode($code)
|
||||||
{
|
{
|
||||||
$data = $this->request('post', 'oauth/token', [
|
return $this->request('post', 'oauth/token', [
|
||||||
'grant_type' => 'authorization_code',
|
'grant_type' => 'authorization_code',
|
||||||
'client_id' => config('bconnect.client_id'),
|
'client_id' => config('bconnect.client_id'),
|
||||||
'client_secret' => config('bconnect.client_secret'),
|
'client_secret' => config('bconnect.client_secret'),
|
||||||
|
@ -156,13 +155,11 @@ class Connect
|
||||||
'redirect_uri' => config('bconnect.redirect_url'),
|
'redirect_uri' => config('bconnect.redirect_url'),
|
||||||
'code' => $code
|
'code' => $code
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAccessTokenFromRefreshToken($refresh_token)
|
public function getAccessTokenFromRefreshToken($refresh_token)
|
||||||
{
|
{
|
||||||
$data = $this->request('post', 'oauth/token', [
|
return $this->request('post', 'oauth/token', [
|
||||||
'grant_type' => 'refresh_token',
|
'grant_type' => 'refresh_token',
|
||||||
'client_id' => config('bconnect.client_id'),
|
'client_id' => config('bconnect.client_id'),
|
||||||
'client_secret' => config('bconnect.client_secret'),
|
'client_secret' => config('bconnect.client_secret'),
|
||||||
|
@ -170,8 +167,6 @@ class Connect
|
||||||
'redirect_uri' => config('bconnect.redirect_url'),
|
'redirect_uri' => config('bconnect.redirect_url'),
|
||||||
'refresh_token' => $refresh_token
|
'refresh_token' => $refresh_token
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUserData($access_token)
|
public function getUserData($access_token)
|
||||||
|
@ -184,7 +179,7 @@ class Connect
|
||||||
$class = get_class($model);
|
$class = get_class($model);
|
||||||
$has_fields = in_array(HasConnectTokens::class, class_uses($class));
|
$has_fields = in_array(HasConnectTokens::class, class_uses($class));
|
||||||
|
|
||||||
if (!$has_fields) {
|
if (! $has_fields) {
|
||||||
throw new ConnectException("$class does not implement HasConnectTokens");
|
throw new ConnectException("$class does not implement HasConnectTokens");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,17 +194,38 @@ class Connect
|
||||||
|
|
||||||
// Sync
|
// Sync
|
||||||
|
|
||||||
public function handleWebhook(Request $request)
|
public function sync(string $event, array $data)
|
||||||
{
|
{
|
||||||
$data = $request->validate([
|
$class = config('bconnect.model');
|
||||||
'event_type' => 'required|in:created,updated,deleted,restored',
|
|
||||||
'connect_data' => 'required|array',
|
|
||||||
'connect_data.id' => 'required'
|
|
||||||
]);
|
|
||||||
|
|
||||||
//@TODO
|
if (in_array($event, ['update', 'delete'])) {
|
||||||
|
$model = $this->resolveUser($data);
|
||||||
|
|
||||||
return ['handled' => false];
|
if (! $model->exists() && $event === 'delete')
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$hasSoftDeletes = in_array(\Illuminate\Database\Eloquent\SoftDeletes::class, class_uses($class));
|
||||||
|
$model = $this->resolveUser($data, $hasSoftDeletes);
|
||||||
|
|
||||||
|
if ($model->exists()) {
|
||||||
|
if ($hasSoftDeletes) {
|
||||||
|
$event = 'restore';
|
||||||
|
} else {
|
||||||
|
$event = 'update';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array(HasConnectWebhook::class, class_uses($class))) {
|
||||||
|
$method = 'onConnect' . ucfirst($event);
|
||||||
|
$model->$method($data);
|
||||||
|
} else {
|
||||||
|
$this->updateUserData($model, $data);
|
||||||
|
$model->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $model;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateUserConnectData($user, $data)
|
public function updateUserConnectData($user, $data)
|
||||||
|
@ -224,20 +240,32 @@ class Connect
|
||||||
$user->fillConnectData($data);
|
$user->fillConnectData($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function resolveUser($data)
|
protected function resolveUser($data, $withTrashed = false)
|
||||||
{
|
{
|
||||||
$model = config('bconnect.model');
|
$class = config('bconnect.model');
|
||||||
|
$query = $class::query();
|
||||||
|
|
||||||
if (in_array(HasConnectData::class, class_uses($model))) {
|
if ($withTrashed)
|
||||||
$origin = is_array($model::$connectIdentifier) ? $model::$connectIdentifier[0] : $model::$connectIdentifier;
|
$query->withTrashed();
|
||||||
$target = is_array($model::$connectIdentifier) ? $model::$connectIdentifier[1] : $model::$connectIdentifier;
|
|
||||||
|
|
||||||
$user = $model::where($target, $data[$origin])->first() ?? new $model;
|
$model = new $class;
|
||||||
|
|
||||||
|
if (in_array(HasConnectData::class, class_uses($class))) {
|
||||||
|
$id = $model->getConnectIdentifier();
|
||||||
|
|
||||||
|
$origin = is_array($id) ? $id[0] : $id;
|
||||||
|
$target = is_array($id) ? $id[1] : $id;
|
||||||
|
|
||||||
|
$model = $query->where($target, $data[$origin])->first() ?? $model;
|
||||||
|
$model->$target = $data[$origin];
|
||||||
} else {
|
} else {
|
||||||
$user = $model::where('email', $data['email'])->first() ?? new $model;
|
$model = $query->where('email', $data['email'])->first() ?? $model;
|
||||||
|
|
||||||
|
if (! $model->exists())
|
||||||
|
$model->email = $data['email'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $user;
|
return $model;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routing
|
// Routing
|
||||||
|
|
|
@ -10,7 +10,7 @@ class ConnectServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
public function register()
|
public function register()
|
||||||
{
|
{
|
||||||
$this->mergeConfigFrom($this->path('src/config/bconnect.php'), 'bconnect');
|
$this->mergeConfigFrom($this->path('config/bconnect.php'), 'bconnect');
|
||||||
|
|
||||||
$this->app->singleton(Connect::class, function ($app) {
|
$this->app->singleton(Connect::class, function ($app) {
|
||||||
return new Connect($app);
|
return new Connect($app);
|
||||||
|
@ -19,7 +19,7 @@ class ConnectServiceProvider extends ServiceProvider
|
||||||
|
|
||||||
public function boot()
|
public function boot()
|
||||||
{
|
{
|
||||||
$config_path = $this->path('src/config/bconnect.php');
|
$config_path = $this->path('config/bconnect.php');
|
||||||
$views_path = $this->path('resources/views/connect');
|
$views_path = $this->path('resources/views/connect');
|
||||||
|
|
||||||
$this->publishes([
|
$this->publishes([
|
||||||
|
@ -50,6 +50,6 @@ class ConnectServiceProvider extends ServiceProvider
|
||||||
|
|
||||||
private function path($path = '')
|
private function path($path = '')
|
||||||
{
|
{
|
||||||
return __DIR__ . "/../../$path";
|
return __DIR__ . "/../$path";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,19 @@ class ConnectController extends Controller
|
||||||
|
|
||||||
public function webhook(Request $request, Connect $connect)
|
public function webhook(Request $request, Connect $connect)
|
||||||
{
|
{
|
||||||
return $connect->handleWebhook($request);
|
$hash = sha1(config('bconnect.client_secret') . date('Y-m-d'));
|
||||||
|
|
||||||
|
if ($request->header('x-connect-hash') !== $hash)
|
||||||
|
abort(403);
|
||||||
|
|
||||||
|
$data = $request->validate([
|
||||||
|
'event_type' => 'required|in:create,update,delete',
|
||||||
|
'connect_data' => 'required|array',
|
||||||
|
'connect_data.*' => 'nullable',
|
||||||
|
'connect_data.id' => 'required',
|
||||||
|
'connect_data.email' => 'required_if:event_type,create|required_if:event_type,update',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$connect->sync($data['event_type'], $data['connect_data']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,47 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Bluesquare\Connect\Traits;
|
namespace Bluesquare\Connect\Traits;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
trait HasConnectData
|
trait HasConnectData
|
||||||
{
|
{
|
||||||
public static $connectIdentifier = 'connect_id';
|
public function getConnectIdentifier()
|
||||||
|
{
|
||||||
protected $connectFillable = [];
|
return $this->connectIdentifier ?? 'connect_id';
|
||||||
|
}
|
||||||
|
|
||||||
public function fillConnectData(array $data)
|
public function fillConnectData(array $data)
|
||||||
{
|
{
|
||||||
foreach ($this->connectFillable as $origin => $target) {
|
$touched = [];
|
||||||
if (is_string($origin)) {
|
|
||||||
$this->$target = $data[$origin] ?? null;
|
$fillable = $this->connectFillable ?? [];
|
||||||
} else {
|
|
||||||
$this->$target = $data[$target] ?? null;
|
foreach ($fillable as $origin => $targets) {
|
||||||
|
$value = is_string($origin) ? $data[$origin] : $data[$targets];
|
||||||
|
$targets = is_string($origin) && is_array($targets) ? $targets : [$targets];
|
||||||
|
|
||||||
|
foreach ($targets as $target) {
|
||||||
|
$parts = explode('|', $target);
|
||||||
|
$target = $parts[0];
|
||||||
|
$currentValue = $value ?? ($parts[1] ?? null);
|
||||||
|
$target_model = $this;
|
||||||
|
$parts = explode('.', $target);
|
||||||
|
|
||||||
|
foreach ($parts as $i => $property) {
|
||||||
|
if ($i < count($parts) - 1) {
|
||||||
|
$target_model = $target_model->$property;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($target_model !== $this)
|
||||||
|
$touched[] = $target_model;
|
||||||
|
|
||||||
|
$target_model->$property = $currentValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract public function fill(array $attributes);
|
foreach ($touched as $model)
|
||||||
|
$model->save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Bluesquare\Connect\Traits;
|
||||||
|
|
||||||
|
trait HasConnectWebhook
|
||||||
|
{
|
||||||
|
public function onConnectCreate(array $data)
|
||||||
|
{
|
||||||
|
$this->onConnectUpdate($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onConnectUpdate(array $data)
|
||||||
|
{
|
||||||
|
if (in_array(HasConnectData::class, class_uses(self::class))) {
|
||||||
|
$this->fillConnectData($data);
|
||||||
|
} else {
|
||||||
|
$this->email = $data['email'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onConnectDelete(array $data)
|
||||||
|
{
|
||||||
|
$this->onConnectUpdate($data);
|
||||||
|
|
||||||
|
if (in_array(\Illuminate\Database\Eloquent\SoftDeletes::class, class_uses(self::class))) {
|
||||||
|
$this->delete();
|
||||||
|
} elseif (array_key_exists('remember_token', $this->attributes)) {
|
||||||
|
$this->remember_token = null;
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onConnectRestore(array $data)
|
||||||
|
{
|
||||||
|
$this->restore();
|
||||||
|
|
||||||
|
$this->onConnectUpdate($data);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue