Merge pull request #8345 from JakubOnderka/oidc-org-uuid

new: [oidc] Allow to create new org with defined UUID
pull/8734/head
Jakub Onderka 2022-11-02 10:16:52 +01:00 committed by GitHub
commit 459759374c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 87 additions and 36 deletions

View File

@ -53,7 +53,7 @@ class UserShell extends AppShell
'help' => __('Check users validity from external identity provider and block not valid user.'),
'parser' => [
'arguments' => [
'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true],
'userId' => ['help' => __('User ID or e-mail address. If not provided, all users will be checked.'), 'required' => false],
],
'options' => [
'block_invalid' => ['help' => __('Block user that are considered invalid.'), 'boolean' => true],
@ -282,7 +282,7 @@ class UserShell extends AppShell
App::uses('Oidc', 'OidcAuth.Lib');
$oidc = new Oidc($this->User);
$conditions = ['disabled' => false]; // fetch just not disabled users
$conditions = ['User.disabled' => false]; // fetch just not disabled users
$userId = isset($this->args[0]) ? $this->args[0] : null;
if ($userId) {

View File

@ -75,16 +75,16 @@ class Organisation extends AppModel
),
);
public $organisationAssociations = array(
'Correlation' => array('table' => 'correlations', 'fields' => array('org_id')),
'Event' => array('table' => 'events', 'fields' => array('org_id', 'orgc_id')),
'Job' => array('table' => 'jobs', 'fields' => array('org_id')),
'Server' => array('table' => 'servers', 'fields' => array('org_id', 'remote_org_id')),
'ShadowAttribute' =>array('table' => 'shadow_attributes', 'fields' => array('org_id', 'event_org_id')),
'SharingGroup' => array('table' => 'sharing_groups', 'fields' => array('org_id')),
'SharingGroupOrg' => array('table' => 'sharing_group_orgs', 'fields' => array('org_id')),
'Thread' => array('table' => 'threads', 'fields' => array('org_id')),
'User' => array('table' => 'users', 'fields' => array('org_id'))
const ORGANISATION_ASSOCIATIONS = array(
'Correlation' => array('table' => 'correlations', 'fields' => array('org_id')),
'Event' => array('table' => 'events', 'fields' => array('org_id', 'orgc_id')),
'Job' => array('table' => 'jobs', 'fields' => array('org_id')),
'Server' => array('table' => 'servers', 'fields' => array('org_id', 'remote_org_id')),
'ShadowAttribute' => array('table' => 'shadow_attributes', 'fields' => array('org_id', 'event_org_id')),
'SharingGroup' => array('table' => 'sharing_groups', 'fields' => array('org_id')),
'SharingGroupOrg' => array('table' => 'sharing_group_orgs', 'fields' => array('org_id')),
'Thread' => array('table' => 'threads', 'fields' => array('org_id')),
'User' => array('table' => 'users', 'fields' => array('org_id'))
);
const GENERIC_MISP_ORGANISATION = [
@ -255,10 +255,11 @@ class Organisation extends AppModel
* @param string $name Organisation name
* @param int $userId Organisation creator
* @param bool $local True if organisation should be marked as local
* @param string|null $uuid UUID of newly created org
* @return int Existing or newly created organisation ID
* @throws Exception
*/
public function createOrgFromName($name, $userId, $local)
public function createOrgFromName($name, $userId, $local, $uuid = null)
{
$existingOrg = $this->find('first', [
'recursive' => -1,
@ -272,7 +273,12 @@ class Organisation extends AppModel
'local' => $local,
'created_by' => $userId,
];
$this->save($organisation);
if ($uuid) {
$organisation['uuid'] = $uuid;
}
if (!$this->save($organisation)) {
throw new Exception("Could not create new org $name");
}
return $this->id;
}
return $existingOrg[$this->alias]['id'];
@ -328,7 +334,7 @@ class Organisation extends AppModel
));
$dataMoved = array('removed_org' => $currentOrg);
$success = true;
foreach ($this->organisationAssociations as $model => $data) {
foreach (self::ORGANISATION_ASSOCIATIONS as $model => $data) {
foreach ($data['fields'] as $field) {
if ($this->isMysql()) {
$sql = 'SELECT `id` FROM `' . $data['table'] . '` WHERE `' . $field . '` = "' . $currentOrg['Organisation']['id'] . '"';

View File

@ -11,6 +11,7 @@ App::uses('Oidc', 'OidcAuth.Lib');
* - OidcAuth.code_challenge_method
* - OidcAuth.role_mapper
* - OidcAuth.organisation_property (default: `organization`)
* - OidcAuth.organisation_uuid_property (default: `organization_uuid`)
* - OidcAuth.roles_property (default: `roles`)
* - OidcAuth.default_org
* - OidcAuth.unblock (boolean, default: false)

View File

@ -49,7 +49,11 @@ class Oidc
$organisationProperty = $this->getConfig('organisation_property', 'organization');
$organisationName = $claims->{$organisationProperty} ?? $this->getConfig('default_org');
$organisationId = $this->checkOrganization($organisationName, $user, $mispUsername);
$organisationUuidProperty = $this->getConfig('organisation_uuid_property', 'organization_uuid');
$organisationUuid = $claims->{$organisationUuidProperty} ?? null;
$organisationId = $this->checkOrganization($organisationName, $organisationUuid, $mispUsername);
if (!$organisationId) {
if ($user) {
$this->block($user);
@ -60,7 +64,7 @@ class Oidc
$roleProperty = $this->getConfig('roles_property', 'roles');
$roles = $claims->{$roleProperty} ?? $oidc->requestUserInfo($roleProperty);
if ($roles === null) {
$this->log($user['email'], "Role property `$roleProperty` is missing in claims.");
$this->log($mispUsername, "Role property `$roleProperty` is missing in claims.");
return false;
}
@ -113,7 +117,7 @@ class Oidc
return $user;
}
$this->log($mispUsername, 'Not found in database.');
$this->log($mispUsername, 'User not found in database.');
$time = time();
$userData = [
@ -125,6 +129,7 @@ class Oidc
'change_pw' => 0,
'date_created' => $time,
'sub' => $sub,
'enable_password' => false, // do not generate default password for user
];
if (!$this->User->save($userData)) {
@ -134,7 +139,7 @@ class Oidc
$refreshToken = $this->getConfig('offline_access', false) ? $oidc->getRefreshToken() : null;
$this->storeMetadata($this->User->id, $claims, $refreshToken);
$this->log($mispUsername, "Saved in database with ID {$this->User->id}");
$this->log($mispUsername, "User saved in database with ID {$this->User->id}");
$this->log($mispUsername, 'Logged in.');
$user = $this->_findUser($settings, ['User.id' => $this->User->id]);
@ -226,7 +231,11 @@ class Oidc
// Check user org
$organisationProperty = $this->getConfig('organisation_property', 'organization');
$organisationName = $claims->{$organisationProperty} ?? $this->getConfig('default_org');
$organisationId = $this->checkOrganization($organisationName, $user, $user['email']);
$organisationUuidProperty = $this->getConfig('organisation_uuid_property', 'organization_uuid');
$organisationUuid = $claims->{$organisationUuidProperty} ?? null;
$organisationId = $this->checkOrganization($organisationName, $organisationUuid, $user['email']);
if (!$organisationId) {
return false;
}
@ -302,44 +311,79 @@ class Oidc
}
/**
* @param string $org
* @param array|null $user
* @param string $orgName Organisation name or UUID
* @param string|null $orgUuid Organisation UUID
* @param string $mispUsername
* @return int
* @throws Exception
*/
private function checkOrganization($org, $user, $mispUsername)
private function checkOrganization($orgName, $orgUuid, $mispUsername)
{
if (empty($org)) {
if (empty($orgName)) {
$this->log($mispUsername, "Organisation name not provided.");
return false;
}
$orgIsUuid = Validation::uuid($org);
if ($orgUuid && !Validation::uuid($orgUuid)) {
$this->log($mispUsername, "Organisation UUID `$orgUuid` is not valid UUID.");
return false;
}
$orgNameIsUuid = Validation::uuid($orgName);
if ($orgUuid) {
$conditions = ['uuid' => strtolower($orgUuid)];
$this->log($mispUsername, "Trying to find user org by UUID `$orgUuid`.");
} else if ($orgNameIsUuid) {
$conditions = ['uuid' => strtolower($orgName)];
$this->log($mispUsername, "Trying to find user org by UUID `$orgName` provided in org name.");
} else {
$conditions = ['name' => $orgName];
$this->log($mispUsername, "Trying to find user org by name `$orgName`.");
}
$orgAux = $this->User->Organisation->find('first', [
'fields' => ['Organisation.id'],
'conditions' => $orgIsUuid ? ['uuid' => strtolower($org)] : ['name' => $org],
'fields' => ['Organisation.id', 'Organisation.name'],
'conditions' => $conditions,
]);
if (empty($orgAux)) {
if ($orgIsUuid) {
$this->log($mispUsername, "Could not found organisation with UUID `$org`.");
// Org does not exists and we don't know org name, so it is not possible to crete a new one.
if ($orgNameIsUuid) {
$this->log($mispUsername, "Could not find organisation with UUID `$orgName`.");
return false;
}
$orgUserId = 1; // By default created by the admin
if ($user) {
$orgUserId = $user['id'];
}
$orgId = $this->User->Organisation->createOrgFromName($org, $orgUserId, true);
$this->log($mispUsername, "User organisation `$org` created with ID $orgId.");
$orgId = $this->User->Organisation->createOrgFromName($orgName, 0, true, $orgUuid);
$this->log($mispUsername, "User organisation `$orgName` created with ID $orgId.");
} else {
$orgId = $orgAux['Organisation']['id'];
$this->log($mispUsername, "User organisation `$org` found with ID $orgId.");
$this->log($mispUsername, "User organisation `$orgName` found with ID $orgId.");
// If org name in database is different, update to new name from OIDC
if ($orgUuid && $orgName != $orgAux['Organisation']['name']) {
$this->changeOrgName($orgId, $orgName);
$this->log($mispUsername, "Changed org name for #$orgId from `{$orgAux['Organisation']['name']}` to `$orgName`.");
}
}
return $orgId;
}
/**
* @param int $orgId
* @param string $newName
* @return void
* @throws Exception
*/
private function changeOrgName($orgId, $newName)
{
$result = $this->User->Organisation->save([
'id' => $orgId,
'name' => $newName,
], true, ['id', 'name']);
if (!$result) {
throw new Exception("Could not rename org with ID $orgId to `$newName`.");
}
}
/**
* @param array $roles Role list provided by OIDC
* @param string $mispUsername