mirror of https://github.com/MISP/MISP
Merge pull request #8345 from JakubOnderka/oidc-org-uuid
new: [oidc] Allow to create new org with defined UUIDpull/8734/head
commit
459759374c
|
@ -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) {
|
||||
|
|
|
@ -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'] . '"';
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue