// SPDX-FileCopyrightText: 2024 John Livingston // // 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 { 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() 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 { 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 { 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() } }