New option to use and configure Prosody mod_firewall WIP (#97):
* new setting * new configuration screen for Peertube admins * include the mod_firewall module * load mod_firewall if enabled * sys admin can disable the firewall config editing by creating a special file on the disk * user documentation
This commit is contained in:
108
client/common/admin/firewall/services/admin-firewall.ts
Normal file
108
client/common/admin/firewall/services/admin-firewall.ts
Normal file
@ -0,0 +1,108 @@
|
||||
// SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { RegisterClientOptions } from '@peertube/peertube-types/client'
|
||||
import type { AdminFirewallConfiguration } from 'shared/lib/types'
|
||||
import {
|
||||
maxFirewallFileSize, maxFirewallNameLength, maxFirewallFiles, firewallNameRegexp
|
||||
} from 'shared/lib/admin-firewall'
|
||||
import { ValidationError, ValidationErrorType } from '../../../lib/models/validation'
|
||||
import { getBaseRoute } from '../../../../utils/uri'
|
||||
|
||||
export class AdminFirewallService {
|
||||
public _registerClientOptions: RegisterClientOptions
|
||||
|
||||
private readonly _headers: any = {}
|
||||
|
||||
constructor (registerClientOptions: RegisterClientOptions) {
|
||||
this._registerClientOptions = registerClientOptions
|
||||
|
||||
this._headers = this._registerClientOptions.peertubeHelpers.getAuthHeader() ?? {}
|
||||
this._headers['content-type'] = 'application/json;charset=UTF-8'
|
||||
}
|
||||
|
||||
async validateConfiguration (adminFirewallConfiguration: AdminFirewallConfiguration): Promise<boolean> {
|
||||
const propertiesError: ValidationError['properties'] = {}
|
||||
|
||||
if (adminFirewallConfiguration.files.length > maxFirewallFiles) {
|
||||
const validationError = new ValidationError(
|
||||
'AdminFirewallConfigurationValidationError',
|
||||
await this._registerClientOptions.peertubeHelpers.translate(LOC_TOO_MANY_ENTRIES),
|
||||
propertiesError
|
||||
)
|
||||
throw validationError
|
||||
}
|
||||
|
||||
const seen = new Map<string, true>()
|
||||
for (const [i, e] of adminFirewallConfiguration.files.entries()) {
|
||||
propertiesError[`files.${i}.name`] = []
|
||||
if (e.name === '') {
|
||||
propertiesError[`files.${i}.name`].push(ValidationErrorType.Missing)
|
||||
} else if (e.name.length > maxFirewallNameLength) {
|
||||
propertiesError[`files.${i}.name`].push(ValidationErrorType.TooLong)
|
||||
} else if (!firewallNameRegexp.test(e.name)) {
|
||||
propertiesError[`files.${i}.name`].push(ValidationErrorType.WrongFormat)
|
||||
} else if (seen.has(e.name)) {
|
||||
propertiesError[`files.${i}.name`].push(ValidationErrorType.Duplicate)
|
||||
} else {
|
||||
seen.set(e.name, true)
|
||||
}
|
||||
|
||||
propertiesError[`files.${i}.content`] = []
|
||||
if (e.content.length > maxFirewallFileSize) {
|
||||
propertiesError[`files.${i}.content`].push(ValidationErrorType.TooLong)
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.values(propertiesError).find(e => e.length > 0)) {
|
||||
const validationError = new ValidationError(
|
||||
'AdminFirewallConfigurationValidationError',
|
||||
await this._registerClientOptions.peertubeHelpers.translate(LOC_VALIDATION_ERROR),
|
||||
propertiesError
|
||||
)
|
||||
throw validationError
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
async saveConfiguration (
|
||||
adminFirewallConfiguration: AdminFirewallConfiguration
|
||||
): Promise<AdminFirewallConfiguration> {
|
||||
if (!await this.validateConfiguration(adminFirewallConfiguration)) {
|
||||
throw new Error('Invalid form data')
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
getBaseRoute(this._registerClientOptions) + '/api/admin/firewall/',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: this._headers,
|
||||
body: JSON.stringify(adminFirewallConfiguration)
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to save configuration.')
|
||||
}
|
||||
|
||||
return response.json()
|
||||
}
|
||||
|
||||
async fetchConfiguration (): Promise<AdminFirewallConfiguration> {
|
||||
const response = await fetch(
|
||||
getBaseRoute(this._registerClientOptions) + '/api/admin/firewall/',
|
||||
{
|
||||
method: 'GET',
|
||||
headers: this._headers
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Can\'t get firewall configuration.')
|
||||
}
|
||||
|
||||
return response.json()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user