new: [keycloak auth] wip version 1 added
- authenticate via keycloak (on demand only at the moment) - check if user from JWT token exists - if yes: - check if role needs to be updated - do so if need be - check if organisation needs to be updated - (currently only captures, not aligned yet!) - if no: - create user - set role (if set, otherwise fall back to default configuration) - capture organisation - (currently not aligned yet!)keycloak
parent
78f193cb5c
commit
efe3765609
|
@ -7,6 +7,11 @@ use Cake\ORM\Table;
|
||||||
use Cake\Validation\Validator;
|
use Cake\Validation\Validator;
|
||||||
use Cake\ORM\RulesChecker;
|
use Cake\ORM\RulesChecker;
|
||||||
use Cake\ORM\TableRegistry;
|
use Cake\ORM\TableRegistry;
|
||||||
|
use \Cake\Datasource\EntityInterface;
|
||||||
|
use \Cake\Http\Session;
|
||||||
|
use Cake\Http\Client;
|
||||||
|
use Cake\Utility\Security;
|
||||||
|
use Cake\Core\Configure;
|
||||||
|
|
||||||
class UsersTable extends AppTable
|
class UsersTable extends AppTable
|
||||||
{
|
{
|
||||||
|
@ -93,4 +98,121 @@ class UsersTable extends AppTable
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function extractKeycloakProfileData($profile_payload)
|
||||||
|
{
|
||||||
|
$mapping = Configure::read('keycloak.mapping');
|
||||||
|
$fields = [
|
||||||
|
'org_uuid' => 'org_uuid',
|
||||||
|
'role_name' => 'role_name',
|
||||||
|
'username' => 'preferred_username',
|
||||||
|
'email' => 'email',
|
||||||
|
'first_name' => 'given_name',
|
||||||
|
'last_name' => 'family_name'
|
||||||
|
];
|
||||||
|
foreach ($fields as $field => $default) {
|
||||||
|
if (!empty($mapping[$field])) {
|
||||||
|
$fields[$field] = $mapping[$field];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$user = [
|
||||||
|
'individual' => [
|
||||||
|
'email' => $profile_payload[$fields['email']],
|
||||||
|
'first_name' => $profile_payload[$fields['first_name']],
|
||||||
|
'last_name' => $profile_payload[$fields['last_name']]
|
||||||
|
],
|
||||||
|
'user' => [
|
||||||
|
'username' => $profile_payload[$fields['username']],
|
||||||
|
],
|
||||||
|
'organisation' => [
|
||||||
|
'uuid' => $profile_payload[$fields['org_uuid']],
|
||||||
|
],
|
||||||
|
'role' => [
|
||||||
|
'name' => $profile_payload[$fields['role_name']],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
$user['user']['individual_id'] = $this->captureIndividual($user);
|
||||||
|
$user['user']['role_id'] = $this->captureRole($user);
|
||||||
|
$existingUser = $this->find()->where(['username' => $user['user']['username']])->first();
|
||||||
|
if (empty($existingUser)) {
|
||||||
|
$user['user']['password'] = Security::randomString(16);
|
||||||
|
$existingUser = $this->newEntity($user['user']);
|
||||||
|
if (!$this->save($existingUser)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$dirty = false;
|
||||||
|
if ($user['user']['individual_id'] != $existingUser['individual_id']) {
|
||||||
|
$existingUser['individual_id'] = $user['user']['individual_id'];
|
||||||
|
$dirty = true;
|
||||||
|
}
|
||||||
|
if ($user['user']['role_id'] != $existingUser['role_id']) {
|
||||||
|
$existingUser['role_id'] = $user['user']['role_id'];
|
||||||
|
$dirty = true;
|
||||||
|
}
|
||||||
|
$existingUser;
|
||||||
|
if ($dirty) {
|
||||||
|
if (!$this->save($existingUser)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $existingUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function captureIndividual($user)
|
||||||
|
{
|
||||||
|
$individual = $this->Individuals->find()->where(['email' => $user['individual']['email']])->first();
|
||||||
|
if (empty($individual)) {
|
||||||
|
$individual = $this->Individuals->newEntity($user['individual']);
|
||||||
|
if (!$this->Individuals->save($individual)) {
|
||||||
|
throw new BadRequestException(__('Could not save the associated individual'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $individual->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function captureOrganisation($user)
|
||||||
|
{
|
||||||
|
$organisation = $this->Organisations->find()->where(['uuid' => $user['organisation']['uuid']])->first();
|
||||||
|
if (empty($organisation)) {
|
||||||
|
$user['organisation']['name'] = $user['organisation']['uuid'];
|
||||||
|
$organisation = $this->Organisations->newEntity($user['organisation']);
|
||||||
|
if (!$this->Organisations->save($organisation)) {
|
||||||
|
throw new BadRequestException(__('Could not save the associated organisation'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $organisation->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function captureRole($user)
|
||||||
|
{
|
||||||
|
$role = $this->Roles->find()->where(['name' => $user['role']['name']])->first();
|
||||||
|
if (empty($role)) {
|
||||||
|
if (!empty(Configure::read('keycloak.default_role_name'))) {
|
||||||
|
$default_role_name = Configure::read('keycloak.default_role_name');
|
||||||
|
$role = $this->Roles->find()->where(['name' => $default_role_name])->first();
|
||||||
|
}
|
||||||
|
if (empty($role)) {
|
||||||
|
throw new NotFoundException(__('Invalid role'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $role->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUser(EntityInterface $profile, Session $session)
|
||||||
|
{
|
||||||
|
$userId = $session->read('Auth.User.id');
|
||||||
|
if ($userId) {
|
||||||
|
return $this->get($userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
$raw_profile_payload = $profile->access_token->getJwt()->getPayload();
|
||||||
|
$user = $this->extractKeycloakProfileData($raw_profile_payload);
|
||||||
|
if (!$user) {
|
||||||
|
throw new \RuntimeException('Unable to save new user');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,5 +7,16 @@
|
||||||
echo $this->Form->submit(__('Submit'), ['class' => 'btn btn-lg btn-primary btn-block']);
|
echo $this->Form->submit(__('Submit'), ['class' => 'btn btn-lg btn-primary btn-block']);
|
||||||
echo $this->Form->end();
|
echo $this->Form->end();
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
|
echo $this->Form->postLink(
|
||||||
|
'Login with Keycloak',
|
||||||
|
[
|
||||||
|
'prefix' => false,
|
||||||
|
'plugin' => 'ADmad/SocialAuth',
|
||||||
|
'controller' => 'Auth',
|
||||||
|
'action' => 'login',
|
||||||
|
'provider' => 'keycloak',
|
||||||
|
'?' => ['redirect' => $this->request->getQuery('redirect')]
|
||||||
|
]
|
||||||
|
);
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue