Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added admin audit log for admin config integration setup #640

Merged
merged 6 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
### Changed
- Add application's support for Nextcloud 30
- Log admin configuration to audit log (`/audit.log`)

## 2.6.3 - 2024-04-17
### Changed
Expand Down
41 changes: 40 additions & 1 deletion lib/Controller/ConfigController.php
Original file line number Diff line number Diff line change
Expand Up @@ -216,11 +216,21 @@ private function setIntegrationConfig(array $values): array {

// creates or replace the app password
if (key_exists('setup_app_password', $values) && $values['setup_app_password'] === true) {
$isAppPasswordBeingReplaced = $this->openprojectAPIService->hasAppPassword();
$this->openprojectAPIService->deleteAppPassword();
if (!$this->userManager->userExists(Application::OPEN_PROJECT_ENTITIES_NAME)) {
throw new NoUserException('User "' . Application::OPEN_PROJECT_ENTITIES_NAME . '" does not exists to create application password');
}
$appPassword = $this->openprojectAPIService->generateAppPasswordTokenForUser();
if($isAppPasswordBeingReplaced) {
$this->openprojectAPIService->logToAuditFile(
"Application password for user 'OpenProject has been replaced' with new password in application " . Application::APP_ID
);
} else {
$this->openprojectAPIService->logToAuditFile(
"New application password for user 'OpenProject' has been created in application " . Application::APP_ID
);
}
}

$oldOpenProjectOauthUrl = $this->config->getAppValue(
Expand Down Expand Up @@ -280,6 +290,11 @@ private function setIntegrationConfig(array $values): array {
// resetting and keeping the project folder setup should delete the user app password
if (key_exists('setup_app_password', $values) && $values['setup_app_password'] === false) {
$this->openprojectAPIService->deleteAppPassword();
if(!$runningFullReset) {
$this->openprojectAPIService->logToAuditFile(
"Project folder setup has been deactivated in application " . Application::APP_ID
);
}
}

$this->config->deleteAppValue(Application::APP_ID, 'oPOAuthTokenRevokeStatus');
Expand Down Expand Up @@ -337,6 +352,9 @@ private function setIntegrationConfig(array $values): array {
// so setting `fresh_project_folder_setup` as true
if ($runningFullReset) {
$this->config->setAppValue(Application::APP_ID, 'fresh_project_folder_setup', "1");
$this->openprojectAPIService->logToAuditFile(
"OpenProject Integration admin configuration has been reset in application " . Application::APP_ID
);
} elseif (key_exists('setup_app_password', $values) && key_exists('setup_project_folder', $values)) {
// for other cases when api has key 'setup_app_password' and 'setup_project_folder' we set it to false
// assuming user has either fully set the integration or partially without project folder/app password
Expand Down Expand Up @@ -366,6 +384,23 @@ private function setIntegrationConfig(array $values): array {
public function setAdminConfig(array $values): DataResponse {
try {
$result = $this->setIntegrationConfig($values);
$isOPOAuthCrdentialSet = key_exists('openproject_client_id', $values) &&
key_exists('openproject_client_secret', $values) &&
$values['openproject_client_id'] &&
$values['openproject_client_secret'];

if(key_exists('openproject_instance_url', $values) &&
$values['openproject_instance_url'] && !$isOPOAuthCrdentialSet) {
// sending admin audit log if admin has changed or added the openproject host url
$this->openprojectAPIService->logToAuditFile(
"OpenProject host url has been set to '" . $values['openproject_instance_url'] . "' in application " . Application::APP_ID
);
}
if($isOPOAuthCrdentialSet) {
$this->openprojectAPIService->logToAuditFile(
SagarGi marked this conversation as resolved.
Show resolved Hide resolved
"OpenProject OAuth credential 'openproject_client_id' and 'openproject_client_secret' has been set in application " . Application::APP_ID
);
}
return new DataResponse($result);
} catch (OpenprojectGroupfolderSetupConflictException $e) {
return new DataResponse([
Expand Down Expand Up @@ -528,7 +563,11 @@ private function storeUserInfo(): array {
* @return DataResponse
*/
public function autoOauthCreation(): DataResponse {
return new DataResponse($this->recreateOauthClientInformation());
$result = $this->recreateOauthClientInformation();
$this->openprojectAPIService->logToAuditFile(
"Nextcloud OAuth credential has been set in application " . Application::APP_ID
);
return new DataResponse($result);
}

private function deleteOauthClient(): void {
Expand Down
41 changes: 40 additions & 1 deletion lib/Service/OpenProjectAPIService.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use OC\Authentication\Events\AppPasswordCreatedEvent;
use OC\Authentication\Token\IProvider;
use OC\User\NoUserException;
use OCA\AdminAudit\AuditLogger;
use OCA\GroupFolders\Folder\FolderManager;
use OCA\OpenProject\AppInfo\Application;
use OCA\OpenProject\Exception\OpenprojectErrorException;
Expand Down Expand Up @@ -49,6 +50,7 @@
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\Log\ILogFactory;
use OCP\PreConditionNotMetException;
use OCP\Security\ISecureRandom;
use OCP\Server;
Expand Down Expand Up @@ -115,6 +117,7 @@ class OpenProjectAPIService {
*/
private ISubAdmin $subAdminManager;
private IDBConnection $db;
private ILogFactory $logFactory;


/**
Expand All @@ -125,6 +128,8 @@ class OpenProjectAPIService {
private IProvider $tokenProvider;
private ISecureRandom $random;
private IEventDispatcher $eventDispatcher;
private AuditLogger $auditLogger;

public function __construct(
string $appName,
IAvatarManager $avatarManager,
Expand All @@ -142,7 +147,8 @@ public function __construct(
ISecureRandom $random,
IEventDispatcher $eventDispatcher,
ISubAdmin $subAdminManager,
IDBConnection $db
IDBConnection $db,
ILogFactory $logFactory,
) {
$this->appName = $appName;
$this->avatarManager = $avatarManager;
Expand All @@ -161,6 +167,7 @@ public function __construct(
$this->random = $random;
$this->eventDispatcher = $eventDispatcher;
$this->db = $db;
$this->logFactory = $logFactory;
}

/**
Expand Down Expand Up @@ -1070,6 +1077,33 @@ class_exists('\OCA\GroupFolders\Folder\FolderManager') &&
);
}

/**
* @param $auditLogMessage
* @return void
*/
public function logToAuditFile($auditLogMessage): void {
if($this->isAdminAuditConfigSetCorrectly()) {
SagarGi marked this conversation as resolved.
Show resolved Hide resolved
$this->auditLogger = new AuditLogger($this->logFactory, $this->config);
$this->auditLogger->info($auditLogMessage,
['app' => 'admin_audit']
);
}
}

public function isAdminAuditConfigSetCorrectly(): bool {
$logLevel = $this->config->getSystemValue('loglevel');
$configAuditFile = $this->config->getSystemValue('logfile_audit');
$logCondition = $this->config->getSystemValue('log.condition');
// All the above config should be satisfied in order for admin audit log for the integration application
// if any of the config is failed to be set then we are not able to send the admin audit logging in the audit.log file
return (
$this->appManager->isInstalled('admin_audit') &&
$configAuditFile === $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/audit.log' &&
(isset($logCondition["apps"]) && in_array('admin_audit', $logCondition["apps"])) &&
$logLevel >= 1
);
}

public function isTermsOfServiceAppEnabled(): bool {
return (
class_exists('\OCA\TermsOfService\Db\Entities\Signatory') &&
Expand All @@ -1081,6 +1115,8 @@ class_exists('\OCA\TermsOfService\Db\Mapper\TermsMapper') &&
);
}



/**
* @return array<mixed>
*/
Expand Down Expand Up @@ -1231,6 +1267,9 @@ public function deleteAppPassword(): void {
foreach ($tokens as $token) {
if ($token->getName() === Application::OPEN_PROJECT_ENTITIES_NAME) {
$this->tokenProvider->invalidateTokenById(Application::OPEN_PROJECT_ENTITIES_NAME, $token->getId());
$this->logToAuditFile(
"Application password for user 'OpenProject has been deleted' in application " . Application::APP_ID
);
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
<referencedClass name="Helmich\JsonAssert\JsonAssertions" />
<!-- these classes belong to the event app, which isn't compulsory, so might not exist while running psalm -->
<referencedClass name="OCP\App\Events\AppEnableEvent" />
<referencedClass name="OCA\AdminAudit\AuditLogger" />

</errorLevel>
</UndefinedClass>
<TooFewArguments>
Expand Down
15 changes: 12 additions & 3 deletions tests/lib/Controller/OpenProjectAPIControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -810,8 +810,11 @@ public function testIsValidOpenProjectInstance(
$response->method('getBody')->willReturn($body);
$service = $this->getMockBuilder(OpenProjectAPIService::class)
->disableOriginalConstructor()
->onlyMethods(['rawRequest'])
->onlyMethods(['rawRequest','isAdminAuditConfigSetCorrectly'])
->getMock();
$service
->method('isAdminAuditConfigSetCorrectly')
->willReturn(false);
$service
->method('rawRequest')
->willReturn($response);
Expand Down Expand Up @@ -873,11 +876,14 @@ public function testIsValidOpenProjectInstanceRedirectNoLocationHeader(): void {
->willReturn('');
$service = $this->getMockBuilder(OpenProjectAPIService::class)
->disableOriginalConstructor()
->onlyMethods(['rawRequest'])
->onlyMethods(['rawRequest','isAdminAuditConfigSetCorrectly'])
->getMock();
$service
->method('rawRequest')
->willReturn($response);
$service
->method('isAdminAuditConfigSetCorrectly')
->willReturn(false);
$controller = new OpenProjectAPIController(
'integration_openproject',
$this->requestMock,
Expand Down Expand Up @@ -971,8 +977,11 @@ public function testIsValidOpenProjectInstanceException(
): void {
$service = $this->getMockBuilder(OpenProjectAPIService::class)
->disableOriginalConstructor()
->onlyMethods(['rawRequest'])
->onlyMethods(['rawRequest', 'isAdminAuditConfigSetCorrectly'])
->getMock();
$service
->method('isAdminAuditConfigSetCorrectly')
->willReturn(false);
$service
->method('rawRequest')
->willThrowException($thrownException);
Expand Down
Loading
Loading