Skip to content

Commit

Permalink
Added admin audit log for admin config integration setup
Browse files Browse the repository at this point in the history
Signed-off-by: sagargurung1001@gmail.com <sagargurung1001@gmail.com>
  • Loading branch information
SagarGi committed Jun 12, 2024
1 parent 0890786 commit f258dbd
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 4 deletions.
33 changes: 32 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->logToAudit(
"Application Password for user 'OpenProject has been replaced' in application" . Application::APP_ID
);
} else {
$this->openprojectAPIService->logToAudit(
"Project folder set up has been switched on in " . 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->logToAudit(
"Project folder setup has been switched off 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->logToAudit(
"OpenProject Integration admin config has been reset in application " . Application::APP_ID, true
);
} 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,15 @@ private function setIntegrationConfig(array $values): array {
public function setAdminConfig(array $values): DataResponse {
try {
$result = $this->setIntegrationConfig($values);
if(key_exists('openproject_client_id', $values) &&
key_exists('openproject_client_secret', $values) &&
$values['openproject_client_id'] &&
$values['openproject_client_secret']
) {
$this->openprojectAPIService->logToAudit(
"OpenProject OAuth credential has been set in application " . Application::APP_ID
);
}
return new DataResponse($result);
} catch (OpenprojectGroupfolderSetupConflictException $e) {
return new DataResponse([
Expand Down Expand Up @@ -526,7 +553,11 @@ private function storeUserInfo(): array {
* @return DataResponse
*/
public function autoOauthCreation(): DataResponse {
return new DataResponse($this->recreateOauthClientInformation());
$result = $this->recreateOauthClientInformation();
$this->openprojectAPIService->logToAudit(
"Nextcloud Oauth credential has been set on application " . Application::APP_ID
);
return new DataResponse($result);
}

private function deleteOauthClient(): void {
Expand Down
11 changes: 11 additions & 0 deletions lib/Controller/OpenProjectAPIController.php
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,9 @@ public function isValidOpenProjectInstance(string $url): DataResponse {
);
return new DataResponse(['result' => 'invalid']);
}
$oldOpenProjectOauthUrl = $this->config->getAppValue(
Application::APP_ID, 'openproject_instance_url', ''
);
try {
$response = $this->openprojectAPIService->rawRequest(
'', $url, '', [], 'GET',
Expand Down Expand Up @@ -585,6 +588,10 @@ public function isValidOpenProjectInstance(string $url): DataResponse {
$decodedBody['_type'] === 'Root' &&
$decodedBody['instanceName'] !== ''
) {
// sending admin audit log if admin has changed or added the openproject host url
$this->openprojectAPIService->logToAudit(
"OpenProject host url has been set or changed to '$url' in application " . Application::APP_ID
);
return new DataResponse(['result' => true]);
}
} catch (ClientException $e) {
Expand All @@ -598,6 +605,10 @@ public function isValidOpenProjectInstance(string $url): DataResponse {
$decodedBody['_type'] === 'Error' &&
$decodedBody['errorIdentifier'] !== ''
) {
// sending admin audit log if admin has changed or added the openproject host url
$this->openprojectAPIService->logToAudit(
"OpenProject host url has been set or changed to '$url' in application " . Application::APP_ID
);
return new DataResponse(['result' => true]);
}
$this->logger->error(
Expand Down
41 changes: 40 additions & 1 deletion lib/Service/OpenProjectAPIService.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,18 @@
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\IL10N;
use OCP\ILogger;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\PreConditionNotMetException;
use OCP\Security\ISecureRandom;
use OCP\Server;
use phpDocumentor\Reflection\Types\This;
use Psr\Log\LoggerInterface;
use Safe\Exceptions\JsonException;
use OCA\AdminAudit\AuditLogger;
use OCP\Log\ILogFactory;


define('CACHE_TTL', 3600);

Expand Down Expand Up @@ -116,6 +121,7 @@ class OpenProjectAPIService {
*/
private ISubAdmin $subAdminManager;
private IDBConnection $db;
private ILogFactory $logFactory;


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

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

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

/**
* @param $auditLogMessage
* @return void
*/
public function logToAudit($auditLogMessage): void {
if($this->isAdminAuditConfigSetCorrectly()) {
$this->auditLogger = new AuditLogger($this->logFactory, $this->config);
$this->auditLogger->info($auditLogMessage,
['app' => 'admin_audit']
);
}
}

public function isAdminAuditConfigSetCorrectly(): bool {
$logLevel = $this->config->getSystemValue('loglevel', ILogger::WARN);
$configAuditFile = $this->config->getSystemValue('logfile_audit', ILogger::WARN);
$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 @@ -1092,6 +1129,8 @@ class_exists('\OCA\TermsOfService\Db\Mapper\TermsMapper') &&
);
}



/**
* @return array<mixed>
*/
Expand Down
4 changes: 3 additions & 1 deletion lib/Settings/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public function getForm(): TemplateResponse {
}
$projectFolderStatusInformation = $this->openProjectAPIService->getProjectFolderSetupInformation();
$isAllTermsOfServiceSignedForUserOpenProject = $this->openProjectAPIService->isAllTermsOfServiceSignedForUserOpenProject();
$isAdminAuditConfigCorrectForIntegrationApp = $this->openProjectAPIService->isAdminAuditConfigSetCorrectly();
$adminConfig = [
'openproject_client_id' => $clientID,
'openproject_client_secret' => $clientSecret,
Expand All @@ -68,7 +69,8 @@ public function getForm(): TemplateResponse {
'app_password_set' => $this->openProjectAPIService->hasAppPassword(),
'project_folder_info' => $projectFolderStatusInformation,
'fresh_project_folder_setup' => $this->config->getAppValue(Application::APP_ID, 'fresh_project_folder_setup', '0') === '1',
'all_terms_of_services_signed' => $isAllTermsOfServiceSignedForUserOpenProject
'all_terms_of_services_signed' => $isAllTermsOfServiceSignedForUserOpenProject,
'is_admin_audit_config_correct' => $isAdminAuditConfigCorrectForIntegrationApp
];

$adminConfigStatus = OpenProjectAPIService::isAdminConfigOk($this->config);
Expand Down
8 changes: 7 additions & 1 deletion src/components/AdminSettings.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<template>
<div id="openproject_prefs" class="section">
<TermsOfServiceUnsigned :is-all-terms-of-service-signed-for-user-open-project="isAllTermsOfServiceSignedForUserOpenProject" />
<SettingsTitle is-setting="admin" />
<SettingsTitle is-setting="admin" :is-audit-logging-warning="!isThereAuditLogWarning" />
<NoteCardInfo :is-audit-logging-warning="!isThereAuditLogWarning" />
<div class="openproject-server-host">
<FormHeading index="1"
:title="t('integration_openproject', 'OpenProject server')"
Expand Down Expand Up @@ -372,6 +373,7 @@ import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
import ProjectFolderError from './admin/ProjectFolderError.vue'
import TermsOfServiceUnsigned from './admin/TermsOfServiceUnsigned.vue'
import NoteCardInfo from './admin/NoteCardInfo.vue'
export default {
name: 'AdminSettings',
components: {
Expand All @@ -389,6 +391,7 @@ export default {
NcCheckboxRadioSwitch,
ProjectFolderError,
TermsOfServiceUnsigned,
NoteCardInfo,
},
data() {
return {
Expand Down Expand Up @@ -507,6 +510,9 @@ export default {
isProjectFolderSetupCompleted() {
return this.isProjectFolderSetupFormInEdit ? false : this.opUserAppPassword
},
isThereAuditLogWarning() {
return this.state.is_admin_audit_config_correct
},
adminFileStorageHref() {
let hostPart = ''
const urlPart = '%sadmin/settings/storages'
Expand Down
58 changes: 58 additions & 0 deletions src/components/admin/NoteCardInfo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<template>
<div v-if="isAuditLoggingWarning" class="notecard">
<div class="notecard--info">
<InformationIcon fill-color="var(--color-info)" />
<p v-html="sanitizedHintText" /> <!-- eslint-disable-line vue/no-v-html -->
</div>
</div>
</template>
<script>
import InformationIcon from 'vue-material-design-icons/Information.vue'
import dompurify from 'dompurify'
export default {
name: 'NoteCardInfo',
components: {
InformationIcon,
},
props: {
isAuditLoggingWarning: {
type: Boolean,
required: false,
},
},
computed: {
getAuditlogDescriptionLinkText() {
const linkText = t('integration_openproject', 'documentation')
const htmlLink = `<a class="link" href="https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/logging_configuration.html#admin-audit-log-optional" target="_blank" title="${linkText}">${linkText}</a>`
return t('integration_openproject', 'To see audit.log in the log for this integration please activate it following this {htmlLink}.', { htmlLink }, null, { escape: false, sanitize: false })
},
sanitizedHintText() {
return dompurify.sanitize(this.getAuditlogDescriptionLinkText, { ADD_ATTR: ['target'] })
},
},
}
</script>
<style lang="scss" scoped>
.notecard {
&--info {
max-width: 900px;
margin: 1rem 0 1rem 0;
padding: 1rem;
border-radius: var(--border-radius);
min-height: 55px;
background-color: var(--color-primary-light);
border-inline-start: 4px solid var(--color-info);
display: flex;
flex-direction: row;
gap: 1rem;
font-weight: inherit;
}
}
</style>
<style>
.notecard--info .link {
color: var(--color-info) !important;
}
</style>

0 comments on commit f258dbd

Please sign in to comment.