-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'pu/ps/t/2513' into 'main'
feature(MatrixSynapseIntegrator): manage matrix user lifecycle See merge request tine20/tine20!6614
- Loading branch information
Showing
14 changed files
with
508 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ | |
* | ||
* @package MatrixSynapseIntegrator | ||
* @license http://www.gnu.org/licenses/agpl.html | ||
* @copyright Copyright (c) 2020 Metaways Infosystems GmbH (http://www.metaways.de) | ||
* @copyright Copyright (c) 2020-2025 Metaways Infosystems GmbH (http://www.metaways.de) | ||
* @author Paul Mehrer <[email protected]> | ||
*/ | ||
|
||
|
@@ -15,13 +15,12 @@ | |
*/ | ||
class MatrixSynapseIntegrator_AllTests | ||
{ | ||
|
||
|
||
public static function suite () | ||
{ | ||
$suite = new PHPUnit\Framework\TestSuite('All MatrixSynapseIntegrator tests'); | ||
|
||
$suite->addTestSuite(MatrixSynapseIntegrator_ControllerTests::class); | ||
$suite->addTestSuite(MatrixSynapseIntegrator_Controller_UserTests::class); | ||
|
||
return $suite; | ||
} | ||
|
26 changes: 26 additions & 0 deletions
26
tests/tine20/MatrixSynapseIntegrator/Backend/CorporalMock.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php | ||
|
||
/** | ||
* MatrixSynapseIntegrator Backend | ||
* | ||
* @package MatrixSynapseIntegrator | ||
* @subpackage Backend | ||
* @license http =>//www.gnu.org/licenses/agpl.html AGPL Version 3 | ||
* @copyright Copyright (c) 2025 Metaways Infosystems GmbH (http://www.metaways.de) | ||
* @author Philipp Schüle <[email protected]> | ||
*/ | ||
|
||
/** | ||
* MatrixSynapseIntegrator Backend Mock | ||
* | ||
* @package MatrixSynapseIntegrator | ||
* @subpackage Backend | ||
*/ | ||
class MatrixSynapseIntegrator_Backend_CorporalMock extends MatrixSynapseIntegrator_Backend_Corporal | ||
{ | ||
public function push(Tinebase_Model_FullUser $user): bool | ||
{ | ||
$this->_policy = $this->_getPolicy($user); | ||
return true; | ||
} | ||
} |
82 changes: 82 additions & 0 deletions
82
tests/tine20/MatrixSynapseIntegrator/Controller/UserTests.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
<?php | ||
|
||
/** | ||
* Tine 2.0 - http://www.tine20.org | ||
* | ||
* @package MatrixSynapseIntegrator | ||
* @subpackage Test | ||
* @license http://www.gnu.org/licenses/agpl.html AGPL Version 3 | ||
* @copyright Copyright (c) 2025 Metaways Infosystems GmbH (http://www.metaways.de) | ||
* @author Philipp Schüle <[email protected]> | ||
*/ | ||
|
||
class MatrixSynapseIntegrator_Controller_UserTests extends TestCase | ||
{ | ||
public function setUp(): void | ||
{ | ||
parent::setUp(); | ||
|
||
MatrixSynapseIntegrator_Controller_User::getInstance()->setBackend(new MatrixSynapseIntegrator_Backend_CorporalMock()); | ||
} | ||
|
||
public function tearDown(): void | ||
{ | ||
parent::tearDown(); | ||
|
||
MatrixSynapseIntegrator_Controller_User::destroyInstance(); | ||
} | ||
|
||
public function testCreateUser(): Tinebase_Model_FullUser | ||
{ | ||
$user = $this->_createTestUser([ | ||
'xprops' => [ | ||
MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ID => '@{user.id}:matrix.domain', | ||
MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ACTIVE => true, | ||
] | ||
]); | ||
|
||
self::assertTrue($user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ACTIVE]); | ||
self::assertEquals('@' . $user->getId() . ':matrix.domain', | ||
$user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ID]); | ||
|
||
// assert corporal policy json | ||
$backend = MatrixSynapseIntegrator_Controller_User::getInstance()->getBackend(); | ||
$policy = $backend->getPushedPolicy(); | ||
self::assertArrayHasKey('users', $policy); | ||
self::assertCount(1, $policy['users']); | ||
self::assertArrayHasKey('authType', $policy['users'][0]); | ||
self::assertEquals('sha1', $policy['users'][0]['authType']); | ||
$userData = $policy['users'][0]; | ||
self::assertEquals($user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ID], $userData['id']); | ||
self::assertEquals($user->accountDisplayName, $userData['displayName']); | ||
return $user; | ||
} | ||
|
||
public function testUpdateUser() | ||
{ | ||
$user = $this->testCreateUser(); | ||
$user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ACTIVE] = false; | ||
$updatedUser = Admin_Controller_User::getInstance()->update($user); | ||
self::assertFalse($updatedUser->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ACTIVE]); | ||
$this->_assertInactiveUserInPolicy(); | ||
} | ||
|
||
protected function _assertInactiveUserInPolicy() | ||
{ | ||
$backend = MatrixSynapseIntegrator_Controller_User::getInstance()->getBackend(); | ||
$policy = $backend->getPushedPolicy(); | ||
self::assertArrayHasKey('users', $policy); | ||
self::assertCount(1, $policy['users']); | ||
self::assertArrayHasKey('active', $policy['users'][0]); | ||
self::assertFalse($policy['users'][0]['active']); | ||
} | ||
|
||
public function testDeleteUser() | ||
{ | ||
$user = $this->testCreateUser(); | ||
// user deletion need the confirmation header | ||
Admin_Controller_User::getInstance()->setRequestContext(['confirm' => true]); | ||
Admin_Controller_User::getInstance()->delete([$user->getId()]); | ||
$this->_assertInactiveUserInPolicy(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
<?php | ||
|
||
/** | ||
* MatrixSynapseIntegrator Backend | ||
* | ||
* @package MatrixSynapseIntegrator | ||
* @subpackage Backend | ||
* @license http =>//www.gnu.org/licenses/agpl.html AGPL Version 3 | ||
* @copyright Copyright (c) 2025 Metaways Infosystems GmbH (http://www.metaways.de) | ||
* @author Philipp Schüle <[email protected]> | ||
*/ | ||
|
||
/** | ||
* MatrixSynapseIntegrator Backend | ||
* | ||
* @package MatrixSynapseIntegrator | ||
* @subpackage Backend | ||
*/ | ||
class MatrixSynapseIntegrator_Backend_Corporal | ||
{ | ||
protected array $_policy = []; | ||
|
||
protected const CORPORAL_ENDPOINT = '_matrix/corporal/policy'; | ||
|
||
public function push(Tinebase_Model_FullUser $user): bool | ||
{ | ||
$this->_policy = $this->_getPolicy($user); | ||
$this->pushPolicyToCorporal($this->_policy); | ||
|
||
return true; | ||
} | ||
|
||
protected function _getHttpClient(): Zend_Http_Client | ||
{ | ||
$matrixHomeServer = MatrixSynapseIntegrator_Config::getInstance()->get( | ||
MatrixSynapseIntegrator_Config::HOME_SERVER_URL); | ||
$corporalUrl = $matrixHomeServer . '/' . self::CORPORAL_ENDPOINT; | ||
|
||
return Tinebase_Core::getHttpClient($corporalUrl); | ||
} | ||
|
||
/** | ||
* @see https://github.com/devture/matrix-corporal/blob/master/docs/http-api.md#policy-submission-endpoint | ||
* | ||
* @param array $policy | ||
* @return bool | ||
* @throws Zend_Http_Client_Exception | ||
*/ | ||
protected function pushPolicyToCorporal(array $policy): bool | ||
{ | ||
$client = $this->_getHttpClient(); | ||
$client->setHeaders([ | ||
'Authorization' => 'Bearer ' . MatrixSynapseIntegrator_Config::getInstance()->get( | ||
MatrixSynapseIntegrator_Config::CORPORAL_SHARED_AUTH_TOKEN), | ||
'Content-Type' => 'application/json', | ||
]); | ||
$client->setRawData(json_encode($policy)); | ||
|
||
if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { | ||
Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ | ||
. ' Pushing policy to ' . $client->getUri()); | ||
} | ||
|
||
$client->request(Zend_Http_Client::PUT); | ||
|
||
$response = $client->getLastResponse(); | ||
if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { | ||
Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ | ||
. ' Response status: ' . $response->getStatus()); | ||
} | ||
|
||
return $response->isSuccessful(); | ||
} | ||
|
||
protected function _getPolicy(Tinebase_Model_FullUser $user): array | ||
{ | ||
// TODO allow to configure defaults/flags | ||
|
||
return [ | ||
"schemaVersion" => 2, | ||
"flags" => [ | ||
"allowCustomUserDisplayNames" => true, | ||
"allowCustomUserAvatars" => true, | ||
"allowCustomPassthroughUserPasswords" => true, | ||
"forbidRoomCreation" => false, | ||
"forbidEncryptedRoomCreation" => false, | ||
"forbidUnencryptedRoomCreation" => false | ||
], | ||
"users" => [ | ||
$this->_getUserPolicy($user) | ||
], | ||
]; | ||
} | ||
|
||
protected function _getUserPolicy(Tinebase_Model_FullUser $user): array | ||
{ | ||
|
||
return [ | ||
"id" => $user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ID], | ||
"active" => $user->is_deleted === 0 && $user->xprops()[MatrixSynapseIntegrator_Config::USER_XPROP_MATRIX_ACTIVE], | ||
"displayName" => $user->accountDisplayName, | ||
"forbidRoomCreation" => false, | ||
"authType" => "sha1", | ||
"authCredential" => Tinebase_User::getInstance()->getPasswordHashByLoginname($user->accountLoginName), | ||
// "authType" => "plain", | ||
// "avatarUri" => "https://example.com/john.jpg", | ||
// "joinedRooms" => [ | ||
// {"roomId": "!roomA:example.com", "powerLevel": 0}, | ||
// {"roomId": "!roomB:example.com", "powerLevel": 50} | ||
// ], | ||
]; | ||
} | ||
|
||
public function getPushedPolicy(): array | ||
{ | ||
return $this->_policy; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,13 @@ | ||
<?php | ||
|
||
/** | ||
* Tine 2.0 | ||
* | ||
* @package MatrixSynapseIntegrator | ||
* @subpackage Config | ||
* @license http://www.gnu.org/licenses/agpl.html AGPL Version 3 | ||
* @author Paul Mehrer <[email protected]> | ||
* @copyright Copyright (c) 2020 Metaways Infosystems GmbH (http://www.metaways.de) | ||
* @copyright Copyright (c) 2020-2025 Metaways Infosystems GmbH (http://www.metaways.de) | ||
*/ | ||
|
||
/** | ||
|
@@ -18,9 +19,14 @@ | |
*/ | ||
class MatrixSynapseIntegrator_Config extends Tinebase_Config_Abstract | ||
{ | ||
const APP_NAME = 'MatrixSynapseIntegrator'; | ||
public const APP_NAME = 'MatrixSynapseIntegrator'; | ||
|
||
public const MATRIX_DOMAIN = 'matrixDomain'; | ||
public const HOME_SERVER_URL = 'homeServerUrl'; | ||
public const CORPORAL_SHARED_AUTH_TOKEN = 'corporalSharedAuthToken'; | ||
|
||
const MATRIX_DOMAIN = 'matrixDomain'; | ||
public const USER_XPROP_MATRIX_ID = 'matrixId'; | ||
public const USER_XPROP_MATRIX_ACTIVE = 'matrixActive'; | ||
|
||
/** | ||
* (non-PHPdoc) | ||
|
@@ -43,6 +49,28 @@ class MatrixSynapseIntegrator_Config extends Tinebase_Config_Abstract | |
self::SETBYADMINMODULE => true, | ||
self::SETBYSETUPMODULE => true, | ||
], | ||
self::HOME_SERVER_URL => [ | ||
//_('Home Server URL') | ||
self::LABEL => 'Home Server URL', | ||
//_('Home Server URL') | ||
self::DESCRIPTION => 'Home Server URL', | ||
self::TYPE => self::TYPE_STRING, | ||
self::CLIENTREGISTRYINCLUDE => true, | ||
self::SETBYADMINMODULE => true, | ||
self::SETBYSETUPMODULE => true, | ||
self::DEFAULT_STR => 'https://matrix.mydomain', | ||
], | ||
self::CORPORAL_SHARED_AUTH_TOKEN => [ | ||
//_('Corporal Shared Auth Token') | ||
self::LABEL => 'Corporal Shared Auth Token', | ||
//_('Corporal Shared Auth Token') | ||
self::DESCRIPTION => 'Corporal Shared Auth Token', | ||
self::TYPE => self::TYPE_STRING, | ||
self::CLIENTREGISTRYINCLUDE => false, | ||
self::SETBYADMINMODULE => false, | ||
self::SETBYSETUPMODULE => false, | ||
self::DEFAULT_STR => 'abc', | ||
], | ||
]; | ||
|
||
/** | ||
|
Oops, something went wrong.