Skip to content

Commit 43fadfc

Browse files
authored
Merge pull request #1 from slowcheetah/lms
Sync & Create Users
2 parents eb30758 + a646b55 commit 43fadfc

19 files changed

+186
-231
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
vendor/
2+
.idea/

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
### [3.1.0] 2022-11-07
6+
7+
* User auto sync interface integration
8+
* User auto create interface integration
9+
510
### [3.0.1] - 2022-09-28
611

712
* Fix #79 (looping on KeycloakCan error). (props: @andrex47)

README.md

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
<img src="https://img.shields.io/packagist/dt/vizir/laravel-keycloak-web-guard.svg" />
44
</p>
55

6+
Fork of [https://github.com/mariovalney/laravel-keycloak-web-guard](https://github.com/mariovalney/laravel-keycloak-web-guard)
7+
68
# Keycloak Web Guard for Laravel
79

810
This packages allow you authenticate users with [Keycloak Server](https://www.keycloak.org).
@@ -19,7 +21,7 @@ It works on front. For APIs we recommend [laravel-keycloak-guard](https://github
1921
This package was tested with:
2022

2123
* Laravel: 5.8 / 7 / 8 / 9
22-
* Keycloak: 18.0.0
24+
* Keycloak: 11.0.3 / 18.0.0
2325

2426
Any other version is not guaranteed to work.
2527

@@ -45,7 +47,7 @@ composer require vizir/laravel-keycloak-web-guard
4547
If you want to change routes or the default values for Keycloak, publish the config file:
4648

4749
```
48-
php artisan vendor:publish --provider="Vizir\KeycloakWebGuard\KeycloakWebGuardServiceProvider"
50+
php artisan vendor:publish --provider="SlowCheetah\KeycloakWebGuard\KeycloakWebGuardServiceProvider"
4951
5052
```
5153

@@ -127,15 +129,17 @@ And change your provider config too:
127129
'providers' => [
128130
'users' => [
129131
'driver' => 'keycloak-users',
130-
'model' => Vizir\KeycloakWebGuard\Models\KeycloakUser::class,
132+
'model' => App\User::class,
133+
'modelSearchField' => 'email', // field in User model for searching
134+
'keyCloakSearchField' => 'id',
135+
'userCreator' => App\KeyCloak\UserCreator::class, // class mast implement SlowCheetah\KeycloakWebGuard\Contracts\CreateUserInterface
136+
'syncUser' => App\KeyCloak\SyncUser::class, // class mast implement SlowCheetah\KeycloakWebGuard\Contracts\SyncUserInterface
131137
],
132138

133139
// ...
134140
]
135141
```
136142

137-
**Note:** if you want use another User Model, check the FAQ *How to implement my Model?*.
138-
139143
## API
140144

141145
We implement the `Illuminate\Contracts\Auth\Guard`. So, all Laravel default methods will be available.
@@ -199,16 +203,6 @@ You can extend it and register your own middleware on Kernel.php or just use `Au
199203

200204
## FAQ
201205

202-
### How to implement my Model?
203-
204-
We registered a new user provider that you configured on `config/auth.php` called "keycloak-users".
205-
206-
In this same configuration you setted the model. So you can register your own model extending `Vizir\KeycloakWebGuard\Models\KeycloakUser` class and changing this configuration.
207-
208-
You can implement your own [User Provider](https://laravel.com/docs/5.8/authentication#adding-custom-user-providers): just remember to implement the `retrieveByCredentials` method receiving the Keycloak Profile information to retrieve a instance of model.
209-
210-
Eloquent/Database User Provider should work well as they will parse the Keycloak Profile and make a "where" to your database. So your user data must match with Keycloak Profile.
211-
212206
### I cannot find my login form.
213207

214208
We register a `login` route to redirect to Keycloak Server. After login we'll receive and proccess the token to authenticate your user.

composer.json

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"name": "vizir/laravel-keycloak-web-guard",
3-
"description": "Simple Keycloak Guard to Laravel Web Routes",
4-
"homepage": "https://github.com/Vizir/laravel-keycloak-web-guard",
2+
"name": "slowcheetah/laravel-keycloak-web-guard",
3+
"description": "Simple Keycloak Guard to Laravel Web Routes With User Sync and User Create Interfaces",
4+
"homepage": "https://github.com/slowcheetah/laravel-keycloak-web-guard",
55
"license": "MIT",
66
"keywords": [
77
"laravel",
@@ -13,6 +13,10 @@
1313
{
1414
"name": "Mário Valney",
1515
"email": "mariovalney@gmail.com"
16+
},
17+
{
18+
"name": "Sergey Nechaev",
19+
"email": "slow_cheetah@inbox.ru"
1620
}
1721
],
1822
"minimum-stability": "dev",
@@ -21,16 +25,16 @@
2125
},
2226
"autoload": {
2327
"psr-4": {
24-
"Vizir\\KeycloakWebGuard\\": "src/"
28+
"SlowCheetah\\KeycloakWebGuard\\": "src/"
2529
}
2630
},
2731
"extra": {
2832
"laravel": {
2933
"providers": [
30-
"Vizir\\KeycloakWebGuard\\KeycloakWebGuardServiceProvider"
34+
"SlowCheetah\\KeycloakWebGuard\\KeycloakWebGuardServiceProvider"
3135
],
3236
"aliases": {
33-
"KeycloakWeb": "Vizir\\KeycloakWebGuard\\Facades\\KeycloakWeb"
37+
"KeycloakWeb": "SlowCheetah\\KeycloakWebGuard\\Facades\\KeycloakWeb"
3438
}
3539
}
3640
}

config/keycloak-web.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
/**
4848
* Page to redirect after callback if there's no "intent"
4949
*
50-
* @see Vizir\KeycloakWebGuard\Controllers\AuthController::callback()
50+
* @see SlowCheetah\KeycloakWebGuard\Controllers\AuthController::callback()
5151
*/
5252
'redirect_url' => '/admin',
5353

@@ -58,7 +58,7 @@
5858
*
5959
* The routes will receive the name "keycloak.{route}" and login/callback are required.
6060
* So, if you make it false, you shoul register a named 'keycloak.login' route and extend
61-
* the Vizir\KeycloakWebGuard\Controllers\AuthController controller.
61+
* the SlowCheetah\KeycloakWebGuard\Controllers\AuthController controller.
6262
*/
6363
'routes' => [
6464
'login' => 'login',

src/Auth/Guard/KeycloakWebGuard.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
<?php
22

3-
namespace Vizir\KeycloakWebGuard\Auth\Guard;
3+
namespace SlowCheetah\KeycloakWebGuard\Auth\Guard;
44

55
use Illuminate\Contracts\Auth\Authenticatable;
66
use Illuminate\Contracts\Auth\Guard;
77
use Illuminate\Http\Request;
88
use Illuminate\Support\Facades\Config;
9-
use Vizir\KeycloakWebGuard\Auth\KeycloakAccessToken;
10-
use Vizir\KeycloakWebGuard\Exceptions\KeycloakCallbackException;
11-
use Vizir\KeycloakWebGuard\Models\KeycloakUser;
12-
use Vizir\KeycloakWebGuard\Facades\KeycloakWeb;
9+
use SlowCheetah\KeycloakWebGuard\Auth\KeycloakAccessToken;
10+
use SlowCheetah\KeycloakWebGuard\Exceptions\KeycloakCallbackException;
11+
use SlowCheetah\KeycloakWebGuard\Models\KeycloakUser;
12+
use SlowCheetah\KeycloakWebGuard\Facades\KeycloakWeb;
1313
use Illuminate\Contracts\Auth\UserProvider;
1414

1515
class KeycloakWebGuard implements Guard

src/Auth/KeycloakAccessToken.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Vizir\KeycloakWebGuard\Auth;
3+
namespace SlowCheetah\KeycloakWebGuard\Auth;
44

55
use Exception;
66

Lines changed: 84 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,82 @@
11
<?php
22

3-
namespace Vizir\KeycloakWebGuard\Auth;
3+
namespace SlowCheetah\KeycloakWebGuard\Auth;
44

55
use Illuminate\Contracts\Auth\Authenticatable;
66
use Illuminate\Contracts\Auth\UserProvider;
7-
use Vizir\KeycloakWebGuard\Models\KeycloakUser;
87

98
class KeycloakWebUserProvider implements UserProvider
109
{
1110
/**
12-
* The user model.
13-
*
11+
* The user model path
1412
* @var string
1513
*/
16-
protected $model;
14+
protected string $model;
1715

1816
/**
19-
* The Constructor
17+
* User model search field
2018
*
21-
* @param string $model
19+
* @var string
2220
*/
23-
public function __construct($model)
24-
{
21+
protected string $userSearchField;
22+
23+
/**
24+
* Key cloak user id field
25+
* @var string
26+
*/
27+
protected string $keyCloakSearchField;
28+
29+
/**
30+
* The user creator class path
31+
* @var string
32+
*/
33+
protected string $userCreator;
34+
35+
/**
36+
* The user sync class path
37+
* @var string
38+
*/
39+
protected string $syncUser;
40+
41+
public function __construct(
42+
string $model,
43+
string $userSearchField,
44+
string $keyCloakSearchField,
45+
string $userCreator,
46+
string $syncUser
47+
) {
2548
$this->model = $model;
49+
$this->userSearchField = $userSearchField;
50+
$this->keyCloakSearchField = $keyCloakSearchField;
51+
$this->userCreator = $userCreator;
52+
$this->syncUser = $syncUser;
2653
}
2754

2855
/**
2956
* Retrieve a user by the given credentials.
3057
*
3158
* @param array $credentials
32-
* @return \Illuminate\Contracts\Auth\Authenticatable|null
59+
* @return Authenticatable|null
3360
*/
34-
public function retrieveByCredentials(array $credentials)
61+
public function retrieveByCredentials(array $credentials): ?Authenticatable
3562
{
36-
$class = '\\'.ltrim($this->model, '\\');
63+
if (!$this->validateCredentialsData($credentials)) {
64+
return null;
65+
}
3766

38-
return new $class($credentials);
67+
$user = $this->getUser($credentials);
68+
if (!$user) {
69+
return null;
70+
}
71+
72+
return $this->syncUser($user, $credentials);
3973
}
4074

4175
/**
4276
* Retrieve a user by their unique identifier.
4377
*
4478
* @param mixed $identifier
45-
* @return \Illuminate\Contracts\Auth\Authenticatable|null
79+
* @return Authenticatable|null
4680
*/
4781
public function retrieveById($identifier)
4882
{
@@ -54,7 +88,7 @@ public function retrieveById($identifier)
5488
*
5589
* @param mixed $identifier
5690
* @param string $token
57-
* @return \Illuminate\Contracts\Auth\Authenticatable|null
91+
* @return Authenticatable|null
5892
*/
5993
public function retrieveByToken($identifier, $token)
6094
{
@@ -64,7 +98,7 @@ public function retrieveByToken($identifier, $token)
6498
/**
6599
* Update the "remember me" token for the given user in storage.
66100
*
67-
* @param \Illuminate\Contracts\Auth\Authenticatable $user
101+
* @param Authenticatable $user
68102
* @param string $token
69103
* @return void
70104
*/
@@ -76,12 +110,45 @@ public function updateRememberToken(Authenticatable $user, $token)
76110
/**
77111
* Validate a user against the given credentials.
78112
*
79-
* @param \Illuminate\Contracts\Auth\Authenticatable $user
113+
* @param Authenticatable $user
80114
* @param array $credentials
81115
* @return bool
82116
*/
83117
public function validateCredentials(Authenticatable $user, array $credentials)
84118
{
85119
throw new \BadMethodCallException('Unexpected method [validateCredentials] call');
86120
}
121+
122+
private function validateCredentialsData(array $credentials): bool
123+
{
124+
return isset($credentials[$this->keyCloakSearchField]);
125+
}
126+
127+
private function createUser(array $credentials): ?Authenticatable
128+
{
129+
/** @var Object $userCreatorClass */
130+
$userCreatorClass = '\\' . ltrim($this->userCreator, '\\');
131+
$userCreator = new $userCreatorClass();
132+
133+
return $userCreator::createUser($credentials);
134+
}
135+
136+
private function getUser(array $credentials): ?Authenticatable
137+
{
138+
$class = '\\' . ltrim($this->model, '\\');
139+
140+
return $class::where($this->userSearchField, $credentials[$this->keyCloakSearchField])
141+
->firstOr(function () use ($credentials) {
142+
return $this->createUser($credentials);
143+
});
144+
}
145+
146+
private function syncUser(Authenticatable $user, array $credentials): Authenticatable
147+
{
148+
/** @var Object $userCreatorClass */
149+
$syncUserClass = '\\' . ltrim($this->syncUser, '\\');
150+
$syncUser = new $syncUserClass();
151+
152+
return $syncUser->sync($user, $credentials);
153+
}
87154
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace SlowCheetah\KeycloakWebGuard\Contracts;
4+
5+
use Illuminate\Contracts\Auth\Authenticatable;
6+
7+
interface CreateUserInterface
8+
{
9+
public static function createUser(array $data): Authenticatable;
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace SlowCheetah\KeycloakWebGuard\Contracts;
4+
5+
use Illuminate\Contracts\Auth\Authenticatable;
6+
7+
interface SyncUserInterface
8+
{
9+
public static function sync(Authenticatable $user, array $data): Authenticatable;
10+
}

0 commit comments

Comments
 (0)