mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
fix(api): skip the mature gate when no birth date is collected (#2582)
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
import {Config} from '../Config';
|
||||||
|
|
||||||
|
let cachedCollectDateOfBirth: boolean | null = null;
|
||||||
|
|
||||||
|
export function getDefaultDateOfBirthCollection(): boolean {
|
||||||
|
return !Config.instance.selfHosted;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function instanceCollectsDateOfBirth(): boolean {
|
||||||
|
return cachedCollectDateOfBirth ?? getDefaultDateOfBirthCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setCachedDateOfBirthCollection(collect: boolean): void {
|
||||||
|
cachedCollectDateOfBirth = collect;
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ import {resolveDeferredPhoneGateEnabled, setCachedDeferredPhoneGateEnabled} from
|
|||||||
import {InstanceConfiguration} from '../Tables';
|
import {InstanceConfiguration} from '../Tables';
|
||||||
import {DEFAULT_DECAY_CONSTANTS, DEFAULT_RENEWAL_CONSTANTS} from '../utils/AttachmentDecay';
|
import {DEFAULT_DECAY_CONSTANTS, DEFAULT_RENEWAL_CONSTANTS} from '../utils/AttachmentDecay';
|
||||||
import {isJsonRecord, parseJsonArray, parseJsonRecord} from '../utils/JsonBoundaryUtils';
|
import {isJsonRecord, parseJsonArray, parseJsonRecord} from '../utils/JsonBoundaryUtils';
|
||||||
|
import {getDefaultDateOfBirthCollection, setCachedDateOfBirthCollection} from './DateOfBirthCollectionCache';
|
||||||
import {normalizeSsoAllowedEmailDomains} from './SsoConfigValidation';
|
import {normalizeSsoAllowedEmailDomains} from './SsoConfigValidation';
|
||||||
|
|
||||||
const GATEWAY_ROLLOUT_CONFIG_KEY = 'gateway_rollout_config';
|
const GATEWAY_ROLLOUT_CONFIG_KEY = 'gateway_rollout_config';
|
||||||
@@ -357,11 +358,21 @@ function getDefaultAppPublicConfig(): InstanceAppPublicConfig {
|
|||||||
privacy_url: null,
|
privacy_url: null,
|
||||||
},
|
},
|
||||||
registration: {
|
registration: {
|
||||||
collect_date_of_birth: !Config.instance.selfHosted,
|
collect_date_of_birth: getDefaultDateOfBirthCollection(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseAppPublicConfig(raw: string): InstanceAppPublicConfig {
|
||||||
|
try {
|
||||||
|
const parsed: unknown = JSON.parse(raw);
|
||||||
|
return normalizeAppPublicConfig(parsed);
|
||||||
|
} catch (error) {
|
||||||
|
Logger.warn({error}, 'Invalid app public config JSON, returning defaults');
|
||||||
|
return getDefaultAppPublicConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeAppPublicConfig(value: unknown): InstanceAppPublicConfig {
|
function normalizeAppPublicConfig(value: unknown): InstanceAppPublicConfig {
|
||||||
const defaults = getDefaultAppPublicConfig();
|
const defaults = getDefaultAppPublicConfig();
|
||||||
if (!isJsonRecord(value)) {
|
if (!isJsonRecord(value)) {
|
||||||
@@ -939,6 +950,7 @@ export class InstanceConfigRepository {
|
|||||||
this.configCache = await this.fetchAllConfigsFromDatabase();
|
this.configCache = await this.fetchAllConfigsFromDatabase();
|
||||||
} while (this.refreshRequested);
|
} while (this.refreshRequested);
|
||||||
this.syncDeferredPhoneGateCache(this.configCache.get(INSTANCE_POLICY_CONFIG_KEY) ?? null);
|
this.syncDeferredPhoneGateCache(this.configCache.get(INSTANCE_POLICY_CONFIG_KEY) ?? null);
|
||||||
|
this.syncDateOfBirthCollectionCache(this.configCache.get(APP_PUBLIC_CONFIG_KEY) ?? null);
|
||||||
})().finally(() => {
|
})().finally(() => {
|
||||||
this.refreshPromise = null;
|
this.refreshPromise = null;
|
||||||
});
|
});
|
||||||
@@ -950,6 +962,11 @@ export class InstanceConfigRepository {
|
|||||||
setCachedDeferredPhoneGateEnabled(resolveDeferredPhoneGateEnabled(policy));
|
setCachedDeferredPhoneGateEnabled(resolveDeferredPhoneGateEnabled(policy));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private syncDateOfBirthCollectionCache(raw: string | null): void {
|
||||||
|
const appPublic = raw ? normalizeAppPublicConfig(parseJsonRecord(raw)) : getDefaultAppPublicConfig();
|
||||||
|
setCachedDateOfBirthCollection(appPublic.registration.collect_date_of_birth);
|
||||||
|
}
|
||||||
|
|
||||||
private updateCachedConfigs(entries: Array<[string, string]>): void {
|
private updateCachedConfigs(entries: Array<[string, string]>): void {
|
||||||
if (!this.configCache) {
|
if (!this.configCache) {
|
||||||
return;
|
return;
|
||||||
@@ -1067,16 +1084,9 @@ export class InstanceConfigRepository {
|
|||||||
|
|
||||||
async getAppPublicConfig(): Promise<InstanceAppPublicConfig> {
|
async getAppPublicConfig(): Promise<InstanceAppPublicConfig> {
|
||||||
const raw = await this.getConfig(APP_PUBLIC_CONFIG_KEY);
|
const raw = await this.getConfig(APP_PUBLIC_CONFIG_KEY);
|
||||||
if (!raw) {
|
const config = raw ? parseAppPublicConfig(raw) : getDefaultAppPublicConfig();
|
||||||
return getDefaultAppPublicConfig();
|
setCachedDateOfBirthCollection(config.registration.collect_date_of_birth);
|
||||||
}
|
return config;
|
||||||
try {
|
|
||||||
const parsed: unknown = JSON.parse(raw);
|
|
||||||
return normalizeAppPublicConfig(parsed);
|
|
||||||
} catch (error) {
|
|
||||||
Logger.warn({error}, 'Invalid app public config JSON, returning defaults');
|
|
||||||
return getDefaultAppPublicConfig();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async setAppPublicConfig(config: {
|
async setAppPublicConfig(config: {
|
||||||
@@ -1105,6 +1115,7 @@ export class InstanceConfigRepository {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
await this.setConfig(APP_PUBLIC_CONFIG_KEY, JSON.stringify(next));
|
await this.setConfig(APP_PUBLIC_CONFIG_KEY, JSON.stringify(next));
|
||||||
|
setCachedDateOfBirthCollection(next.registration.collect_date_of_birth);
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
import {afterEach, describe, expect, it} from 'vitest';
|
||||||
|
import {setCachedDateOfBirthCollection} from '../instance/DateOfBirthCollectionCache';
|
||||||
|
import {canUserAccessNsfwContent} from './AgeUtils';
|
||||||
|
|
||||||
|
const ADULT_DATE_OF_BIRTH = '1990-01-01';
|
||||||
|
const MINOR_DATE_OF_BIRTH = '2020-01-01';
|
||||||
|
|
||||||
|
describe('canUserAccessNsfwContent', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
setCachedDateOfBirthCollection(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows a bot whatever the instance collects', () => {
|
||||||
|
setCachedDateOfBirthCollection(true);
|
||||||
|
expect(canUserAccessNsfwContent({isBot: true, dateOfBirth: null})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows an adult when the instance collects a date of birth', () => {
|
||||||
|
setCachedDateOfBirthCollection(true);
|
||||||
|
expect(canUserAccessNsfwContent({isBot: false, dateOfBirth: ADULT_DATE_OF_BIRTH})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('blocks a minor when the instance collects a date of birth', () => {
|
||||||
|
setCachedDateOfBirthCollection(true);
|
||||||
|
expect(canUserAccessNsfwContent({isBot: false, dateOfBirth: MINOR_DATE_OF_BIRTH})).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('blocks a missing date of birth when the instance collects one', () => {
|
||||||
|
setCachedDateOfBirthCollection(true);
|
||||||
|
expect(canUserAccessNsfwContent({isBot: false, dateOfBirth: null})).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows a missing date of birth when the instance collects none', () => {
|
||||||
|
setCachedDateOfBirthCollection(false);
|
||||||
|
expect(canUserAccessNsfwContent({isBot: false, dateOfBirth: null})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows an account with a minor date of birth when the instance collects none', () => {
|
||||||
|
setCachedDateOfBirthCollection(false);
|
||||||
|
expect(canUserAccessNsfwContent({isBot: false, dateOfBirth: MINOR_DATE_OF_BIRTH})).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
import {instanceCollectsDateOfBirth} from '../instance/DateOfBirthCollectionCache';
|
||||||
|
|
||||||
export function calculateAge(
|
export function calculateAge(
|
||||||
dateOfBirth:
|
dateOfBirth:
|
||||||
| {
|
| {
|
||||||
@@ -53,5 +55,8 @@ export function canUserAccessNsfwContent(user: NsfwEligibilityUser): boolean {
|
|||||||
if (user.isBot) {
|
if (user.isBot) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (!instanceCollectsDateOfBirth()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return isUserAdult(user.dateOfBirth);
|
return isUserAdult(user.dateOfBirth);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user