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

[WP#57970]Add warning when encryption for group folders are not enabled explicitly #713

Open
wants to merge 5 commits into
base: release/2.7
Choose a base branch
from
Open
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 @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Make error handling better in `integration_setup.sh` file for integration configuration setup.
- Improve UI by using Nextcloud's `NoteCard` in project folder setup error.
- Resolve the issue with retrieving the Nextcloud server version for version compare
- Add warning UI when encryption is not explicitly enabled for `groupfolders`.

## 2.7.0 - 2024-09-10
### Changed
Expand Down
13 changes: 12 additions & 1 deletion lib/Service/OpenProjectAPIService.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use OCA\TermsOfService\Db\Mapper\TermsMapper;
use OCP\App\IAppManager;
use OCP\AppFramework\Http;
use OCP\Encryption\IManager;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\InvalidPathException;
use OCP\Files\IRootFolder;
Expand Down Expand Up @@ -119,6 +120,7 @@ class OpenProjectAPIService {
private ISubAdmin $subAdminManager;
private IDBConnection $db;
private ILogFactory $logFactory;
private IManager $encryptionManager;


/**
Expand Down Expand Up @@ -150,6 +152,7 @@ public function __construct(
ISubAdmin $subAdminManager,
IDBConnection $db,
ILogFactory $logFactory,
IManager $encryptionManager
) {
$this->appName = $appName;
$this->avatarManager = $avatarManager;
Expand All @@ -169,6 +172,7 @@ public function __construct(
$this->eventDispatcher = $eventDispatcher;
$this->db = $db;
$this->logFactory = $logFactory;
$this->encryptionManager = $encryptionManager;
}

/**
Expand Down Expand Up @@ -1145,7 +1149,14 @@ class_exists('\OCA\TermsOfService\Db\Mapper\TermsMapper') &&
);
}


public function isServerSideEncryptionEnabled(): bool {
$isEncryptionForHomeStorageEnabled = $this->config->getAppValue('encryption', 'encryptHomeStorage', '0') === '1';
return (
$this->appManager->isInstalled('encryption') &&
$this->encryptionManager->isEnabled() &&
$isEncryptionForHomeStorageEnabled
);
}

/**
* @return array<mixed>
Expand Down
6 changes: 5 additions & 1 deletion lib/Settings/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ public function getForm(): TemplateResponse {
'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,
'admin_audit_configuration_correct' => $isAdminAuditConfigurationSetUpCorrectly
'admin_audit_configuration_correct' => $isAdminAuditConfigurationSetUpCorrectly,
'encryption_info' => [
'server_side_encryption_enabled' => $this->openProjectAPIService->isServerSideEncryptionEnabled(),
'encryption_enabled_for_groupfolders' => $this->config->getAppValue('groupfolders', 'enable_encryption', '') === 'true'
]
];

$adminConfigStatus = OpenProjectAPIService::isAdminConfigOk($this->config);
Expand Down
22 changes: 21 additions & 1 deletion src/components/AdminSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@
:title="t('integration_openproject', 'Project folders (recommended)')"
:is-setup-complete-without-project-folders="isSetupCompleteWithoutProjectFolders"
:is-there-error-after-project-folder-and-app-password-setup="isThereErrorAfterProjectFolderAndAppPasswordSetup"
:show-encryption-warning-for-group-folders="showEncryptionWarningForGroupFolders"
:is-complete="isProjectFolderSetupCompleted"
:is-disabled="isProjectFolderSetUpInDisableMode"
:is-dark-theme="isDarkTheme" />
Expand Down Expand Up @@ -265,6 +266,12 @@
</p>
<p class="note-card--error-description" v-html="projectFolderSetUpErrorMessageDescription(state.project_folder_info.errorMessage)" /> <!-- eslint-disable-line vue/no-v-html -->
</NcNoteCard>
<NcNoteCard v-else-if="showEncryptionWarningForGroupFolders" class="note-card" type="warning">
<p class="note-card--title">
<b>{{ t('integration_openproject', 'Encryption for the Group Folders App is not enabled.') }}</b>
</p>
<p class="note-card--warning-description" v-html="getGroupFoldersEncryptionWarningHint" /> <!-- eslint-disable-line vue/no-v-html -->
</NcNoteCard>
<div class="form-actions">
<NcButton
data-test-id="edit-project-folder-setup"
Expand Down Expand Up @@ -551,6 +558,11 @@ export default {
const hintTextForAdminAudit = t('integration_openproject', 'To activate audit logs for the OpenProject integration, please enable the {htmlLinkForAdminAudit} app and follow the configuration steps outlined in the {htmlLinkForDocumentaion}.', { htmlLinkForAdminAudit, htmlLinkForDocumentaion }, null, { escape: false, sanitize: false })
return dompurify.sanitize(hintTextForAdminAudit, { ADD_ATTR: ['target'] })
},
getGroupFoldersEncryptionWarningHint() {
const linkText = t('integration_openproject', 'documentation')
const htmlLink = `<a class="link" href="https://www.openproject.org/docs/system-admin-guide/integrations/nextcloud/#files-are-not-encrypted-when-using-nextcloud-server-side-encryption" target="_blank" title="${linkText}">${linkText}</a>`
return t('integration_openproject', 'Server-side encryption is active, but encryption for Group Folders is not yet enabled. To ensure secure storage of files in project folders, please follow the configuration steps in the {htmlLink}.', { htmlLink }, null, { escape: false, sanitize: false })
},
isIntegrationComplete() {
return (this.isServerHostFormComplete
&& this.isOPOAuthFormComplete
Expand All @@ -571,6 +583,13 @@ export default {
isResetButtonDisabled() {
return !(this.state.openproject_client_id || this.state.openproject_client_secret || this.state.openproject_instance_url)
},
showEncryptionWarningForGroupFolders() {
if (!this.isProjectFolderAlreadySetup || !this.state.app_password_set || this.isProjectFolderSetupFormInEdit) {
return false
}
return this.state.encryption_info.server_side_encryption_enabled
&& !this.state.encryption_info.encryption_enabled_for_groupfolders
},
},
created() {
this.init()
Expand Down Expand Up @@ -727,6 +746,7 @@ export default {
const success = await this.saveOPOptions()
if (success) {
this.setProjectFolderSetupToViewMode()
this.isProjectFolderAlreadySetup = true
if ((this.formMode.opUserAppPassword === F_MODES.DISABLE && !this.opUserAppPassword) || this.formMode.opUserAppPassword === F_MODES.DISABLE) {
this.formMode.opUserAppPassword = F_MODES.EDIT
}
Expand Down Expand Up @@ -1198,7 +1218,7 @@ export default {
}
.note-card {
max-width: 900px;
&--info-description, &--error-description {
&--info-description, &--error-description, &--warning-description {
.link {
color: #1a67a3 !important;
font-style: normal;
Expand Down
27 changes: 23 additions & 4 deletions src/components/admin/FormHeading.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
<div v-if="isProjectFolderSetupHeading && isSetupCompleteWithoutProjectFolders" class="setup-complete-without-project-folders">
<MinusThickIcon :fill-color="isDarkTheme ? '#000000' : '#FFFFFF'" :size="12" />
</div>
<div v-else-if="isThereErrorAfterProjectFolderAndAppPasswordSetup" class="project-folder-setup-status">
<div v-else-if="isThereErrorAfterProjectFolderAndAppPasswordSetup" class="project-folder-setup-error">
<ExclamationThickIcon fill-color="#FFFFFF" :size="12" />
</div>
<div v-else-if="showEncryptionWarningForGroupFolders" class="project-folder-setup-warning">
<ExclamationThickIcon fill-color="#FFFFFF" :size="12" />
</div>
<div v-else-if="isComplete" class="complete">
Expand All @@ -21,7 +24,8 @@
<div class="title"
:class="{
'green-text': isComplete,
'red-text': isThereErrorAfterProjectFolderAndAppPasswordSetup
'red-text': isThereErrorAfterProjectFolderAndAppPasswordSetup,
'warn-text': showEncryptionWarningForGroupFolders
}">
{{ title }}
</div>
Expand Down Expand Up @@ -68,6 +72,10 @@ export default {
type: Boolean,
default: false,
},
showEncryptionWarningForGroupFolders: {
type: Boolean,
default: false,
},
isDarkTheme: {
type: Boolean,
default: false,
Expand All @@ -90,6 +98,10 @@ export default {
color: var(--color-error);
}

.warn-text {
color: var(--color-warning);
}

.complete {
height: 16px;
width: 16px;
Expand All @@ -100,16 +112,23 @@ export default {
align-items: center;
}

.project-folder-setup-status {
.project-folder-setup-error, .project-folder-setup-warning {
height: 16px;
width: 16px;
border-radius: 50%;
background-color: var(--color-error);
display: flex;
justify-content: center;
align-items: center;
}

.project-folder-setup-error {
background: var(--color-error);
}

.project-folder-setup-warning {
background: var(--color-warning);
}

.setup-complete-without-project-folders {
height: 16px;
width: 16px;
Expand Down
72 changes: 70 additions & 2 deletions tests/jest/components/AdminSettings.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ const selectors = {
projectFolderErrorMessageDetails: '.note-card--error-description',
userAppPasswordButton: '[data-test-id="reset-user-app-password"]',
setupIntegrationDocumentationLinkSelector: '.settings--documentation-info',
adminAuditNoteCardInforSelector: '[type="info"]',
adminAuditNoteCardInfoSelector: '[type="info"]',
encryptionNoteCardWarningSelector: '.notecard--warning',
}

const completeIntegrationState = {
Expand Down Expand Up @@ -1167,6 +1168,10 @@ describe('AdminSettings.vue', () => {
},
fresh_project_folder_setup: true,
app_password_set: false,
encryption_info: {
server_side_encryption_enabled: false,
encryption_enabled_for_groupfolders: false,
},
},
isGroupFolderAlreadySetup: null,
})
Expand Down Expand Up @@ -1245,6 +1250,10 @@ describe('AdminSettings.vue', () => {
status: true,
},
app_password_set: true,
encryption_info: {
server_side_encryption_enabled: false,
encryption_enabled_for_groupfolders: false,
},
},
})
await wrapper.setData({
Expand Down Expand Up @@ -1373,6 +1382,10 @@ describe('AdminSettings.vue', () => {
status: true,
},
app_password_set: true,
encryption_info: {
server_side_encryption_enabled: false,
encryption_enabled_for_groupfolders: false,
},
},
isGroupFolderAlreadySetup: null,
})
Expand Down Expand Up @@ -1554,6 +1567,61 @@ describe('AdminSettings.vue', () => {
})
})

describe('Encryption warning after project folder setup', () => {
beforeEach(async () => {
axios.put.mockReset()
axios.get.mockReset()
})

it.each([
[
'should show warning when server side encryption is enabled but encryption for groupfolders is not enabled',
{
server_side_encryption_enabled: true,
encryption_enabled_for_groupfolders: false,
},
true,
],
[
'should not show warning when server side encryption and groupfolders encryption is enabled',
{
server_side_encryption_enabled: true,
encryption_enabled_for_groupfolders: true,
},
false,
],
[
'should not show warning when server side encryption not enabled but groupfolders encryption is enabled',
{
server_side_encryption_enabled: false,
encryption_enabled_for_groupfolders: true,
},
false,
],
])('%s', (name, encryptionInfoState, expectedResult) => {
const wrapper = getMountedWrapper({
state: {
openproject_instance_url: 'http://openproject.com',
openproject_client_id: 'some-client-id-here',
openproject_client_secret: 'some-client-secret-here',
default_enable_unified_search: false,
default_enable_navigation: false,
nc_oauth_client: {
nextcloud_client_id: 'some-nc-client-id-here',
nextcloud_client_secret: 'some-nc-client-secret-here',
},
project_folder_info: {
status: true,
},
app_password_set: true,
encryption_info: encryptionInfoState,
},
})
const encryptionWarningNoteCard = wrapper.find(selectors.encryptionNoteCardWarningSelector)
expect(encryptionWarningNoteCard.exists()).toBe(expectedResult)
})
})

describe('reset button', () => {
describe('reset all app settings', () => {
let wrapper
Expand Down Expand Up @@ -1890,7 +1958,7 @@ describe('AdminSettings.vue', () => {
],
])('%s', (name, state, expectedResult) => {
const wrapper = getWrapper({ state })
const adminAuditLogNoteCard = wrapper.find(selectors.adminAuditNoteCardInforSelector)
const adminAuditLogNoteCard = wrapper.find(selectors.adminAuditNoteCardInfoSelector)
expect(adminAuditLogNoteCard.exists()).toBe(expectedResult)
})
})
Expand Down
Loading
Loading