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
iglocska 2021-09-24 01:48:50 +02:00
parent 78f193cb5c
commit efe3765609
No known key found for this signature in database
GPG Key ID: BEA224F1FEF113AC
2 changed files with 133 additions and 0 deletions

View File

@ -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;
}
} }

View File

@ -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>