Remove caller-managed Calendar secret references

This commit is contained in:
2026-07-21 12:12:35 +02:00
parent 1df2a1a730
commit 7098751f7f
3 changed files with 14 additions and 32 deletions

View File

@@ -78,7 +78,6 @@ export type CalendarSyncSource = {
display_name?: string | null; display_name?: string | null;
auth_type: CalendarCalDavAuthType; auth_type: CalendarCalDavAuthType;
username?: string | null; username?: string | null;
credential_ref?: string | null;
has_credential: boolean; has_credential: boolean;
sync_enabled: boolean; sync_enabled: boolean;
sync_interval_seconds: number; sync_interval_seconds: number;
@@ -114,7 +113,6 @@ export type CalendarCalDavDiscoveryPayload = {
source_id?: string | null; source_id?: string | null;
auth_type?: CalendarCalDavAuthType | null; auth_type?: CalendarCalDavAuthType | null;
username?: string | null; username?: string | null;
credential_ref?: string | null;
password?: string | null; password?: string | null;
bearer_token?: string | null; bearer_token?: string | null;
}; };
@@ -128,7 +126,6 @@ export type CalendarSyncSourceCreatePayload = {
display_name?: string | null; display_name?: string | null;
auth_type?: CalendarCalDavAuthType; auth_type?: CalendarCalDavAuthType;
username?: string | null; username?: string | null;
credential_ref?: string | null;
password?: string | null; password?: string | null;
bearer_token?: string | null; bearer_token?: string | null;
sync_enabled?: boolean; sync_enabled?: boolean;

View File

@@ -17,6 +17,7 @@ import {
Dialog, Dialog,
DismissibleAlert, DismissibleAlert,
LoadingFrame, LoadingFrame,
PasswordField,
SegmentedControl, SegmentedControl,
TimeField, TimeField,
ToggleSwitch, ToggleSwitch,
@@ -84,7 +85,6 @@ type CalendarCalDavFormPayload = {
display_name: string; display_name: string;
auth_type: CalendarCalDavAuthType; auth_type: CalendarCalDavAuthType;
username: string; username: string;
credential_ref: string;
password: string; password: string;
bearer_token: string; bearer_token: string;
sync_enabled: boolean; sync_enabled: boolean;
@@ -1343,7 +1343,6 @@ function CalendarCollectionDialog({
const [displayName, setDisplayName] = useState(source?.display_name ?? ""); const [displayName, setDisplayName] = useState(source?.display_name ?? "");
const [authType, setAuthType] = useState<CalendarCalDavAuthType>(source?.auth_type ?? "basic"); const [authType, setAuthType] = useState<CalendarCalDavAuthType>(source?.auth_type ?? "basic");
const [username, setUsername] = useState(source?.username ?? ""); const [username, setUsername] = useState(source?.username ?? "");
const [credentialRef, setCredentialRef] = useState(source?.credential_ref ?? "");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [bearerToken, setBearerToken] = useState(""); const [bearerToken, setBearerToken] = useState("");
const [syncEnabled, setSyncEnabled] = useState(source?.sync_enabled ?? true); const [syncEnabled, setSyncEnabled] = useState(source?.sync_enabled ?? true);
@@ -1361,8 +1360,8 @@ function CalendarCollectionDialog({
const effectiveCollectionUrl = (collectionUrl || davUrl).trim(); const effectiveCollectionUrl = (collectionUrl || davUrl).trim();
const effectiveAuthType = sourceMode === "graph" ? "bearer" : authType; const effectiveAuthType = sourceMode === "graph" ? "bearer" : authType;
const needsSourceSecret = sourceMode !== "local" && ( const needsSourceSecret = sourceMode !== "local" && (
effectiveAuthType === "basic" && !source?.has_credential && !credentialRef.trim() && !password.trim() || effectiveAuthType === "basic" && !source?.has_credential && !password.trim() ||
effectiveAuthType === "bearer" && !source?.has_credential && !credentialRef.trim() && !bearerToken.trim()); effectiveAuthType === "bearer" && !source?.has_credential && !bearerToken.trim());
const sourceDetailsInvalid = sourceMode !== "local" && ( const sourceDetailsInvalid = sourceMode !== "local" && (
!isExistingSyncSource && !canEditSource || !isExistingSyncSource && !canEditSource ||
@@ -1385,7 +1384,6 @@ function CalendarCollectionDialog({
displayName, displayName,
authType, authType,
username, username,
credentialRef,
password, password,
bearerToken, bearerToken,
syncEnabled, syncEnabled,
@@ -1413,7 +1411,6 @@ function CalendarCollectionDialog({
display_name: displayName, display_name: displayName,
auth_type: effectiveAuthType, auth_type: effectiveAuthType,
username, username,
credential_ref: credentialRef,
password, password,
bearer_token: bearerToken, bearer_token: bearerToken,
sync_enabled: syncEnabled, sync_enabled: syncEnabled,
@@ -1494,8 +1491,7 @@ function CalendarCollectionDialog({
url, url,
source_id: source?.id ?? null, source_id: source?.id ?? null,
auth_type: authType, auth_type: authType,
username: authType === "basic" ? username.trim() || null : null, username: authType === "basic" ? username.trim() || null : null
credential_ref: credentialRef.trim() || null
}; };
if (authType === "basic" && password.trim()) payload.password = password; if (authType === "basic" && password.trim()) payload.password = password;
if (authType === "bearer" && bearerToken.trim()) payload.bearer_token = bearerToken; if (authType === "bearer" && bearerToken.trim()) payload.bearer_token = bearerToken;
@@ -1613,15 +1609,15 @@ function CalendarCollectionDialog({
<input value={username} onChange={(item) => setUsername(item.target.value)} required={sourceMode !== "local" && effectiveAuthType === "basic"} maxLength={255} disabled={saving || !canEditSource} /> <input value={username} onChange={(item) => setUsername(item.target.value)} required={sourceMode !== "local" && effectiveAuthType === "basic"} maxLength={255} disabled={saving || !canEditSource} />
</label> </label>
<label> <label>
<span>{source?.has_credential || credentialRef.trim() ? "i18n:govoplan-calendar.replace_password.3f912c9c" : "i18n:govoplan-calendar.password.8be3c943"}</span> <span>{source?.has_credential ? "i18n:govoplan-calendar.replace_password.3f912c9c" : "i18n:govoplan-calendar.password.8be3c943"}</span>
<input type="password" value={password} onChange={(item) => setPassword(item.target.value)} disabled={saving || !canEditSource} autoComplete="new-password" /> <PasswordField value={password} onValueChange={setPassword} disabled={saving || !canEditSource} autoComplete="new-password" />
</label> </label>
</> </>
} }
{effectiveAuthType === "bearer" && {effectiveAuthType === "bearer" &&
<label> <label>
<span>{source?.has_credential || credentialRef.trim() ? "i18n:govoplan-calendar.replace_token.bbeee6a9" : "i18n:govoplan-calendar.bearer_token.ffa64bcf"}</span> <span>{source?.has_credential ? "i18n:govoplan-calendar.replace_token.bbeee6a9" : "i18n:govoplan-calendar.bearer_token.ffa64bcf"}</span>
<input type="password" value={bearerToken} onChange={(item) => setBearerToken(item.target.value)} disabled={saving || !canEditSource} autoComplete="new-password" /> <PasswordField value={bearerToken} onValueChange={setBearerToken} disabled={saving || !canEditSource} autoComplete="new-password" />
</label> </label>
} }
<div className={sourceMode === "caldav" ? "calendar-discovery-actions" : "calendar-discovery-actions is-readonly"}> <div className={sourceMode === "caldav" ? "calendar-discovery-actions" : "calendar-discovery-actions is-readonly"}>
@@ -1653,10 +1649,6 @@ function CalendarCollectionDialog({
<span>i18n:govoplan-calendar.display_name.c7874aaa</span> <span>i18n:govoplan-calendar.display_name.c7874aaa</span>
<input value={displayName} onChange={(item) => setDisplayName(item.target.value)} maxLength={255} disabled={saving || !canEditMutableSourceSettings} /> <input value={displayName} onChange={(item) => setDisplayName(item.target.value)} maxLength={255} disabled={saving || !canEditMutableSourceSettings} />
</label> </label>
<label>
<span>i18n:govoplan-calendar.credential_reference.a7e92de5</span>
<input value={credentialRef} onChange={(item) => setCredentialRef(item.target.value)} placeholder="env:CALDAV_PASSWORD" maxLength={255} disabled={saving || !canEditSource} />
</label>
</div> </div>
<div className="calendar-sync-settings"> <div className="calendar-sync-settings">
<ToggleSwitch label="i18n:govoplan-calendar.automatic_sync.084644b2" checked={syncEnabled} disabled={saving || !canEditMutableSourceSettings} onChange={setSyncEnabled} /> <ToggleSwitch label="i18n:govoplan-calendar.automatic_sync.084644b2" checked={syncEnabled} disabled={saving || !canEditMutableSourceSettings} onChange={setSyncEnabled} />
@@ -1686,7 +1678,7 @@ function CalendarCollectionDialog({
<div><dt>i18n:govoplan-calendar.last_attempt.82aee111</dt><dd>{source.last_attempt_at ? dateTimeLabel(new Date(source.last_attempt_at)) : "i18n:govoplan-calendar.never.80c3052d"}</dd></div> <div><dt>i18n:govoplan-calendar.last_attempt.82aee111</dt><dd>{source.last_attempt_at ? dateTimeLabel(new Date(source.last_attempt_at)) : "i18n:govoplan-calendar.never.80c3052d"}</dd></div>
<div><dt>i18n:govoplan-calendar.last_sync.ef0ef267</dt><dd>{source.last_synced_at ? dateTimeLabel(new Date(source.last_synced_at)) : "i18n:govoplan-calendar.never.80c3052d"}</dd></div> <div><dt>i18n:govoplan-calendar.last_sync.ef0ef267</dt><dd>{source.last_synced_at ? dateTimeLabel(new Date(source.last_synced_at)) : "i18n:govoplan-calendar.never.80c3052d"}</dd></div>
<div><dt>i18n:govoplan-calendar.next_sync.88c7af72</dt><dd>{source.next_sync_at && source.sync_enabled ? dateTimeLabel(new Date(source.next_sync_at)) : "i18n:govoplan-calendar.not_scheduled.9c367369"}</dd></div> <div><dt>i18n:govoplan-calendar.next_sync.88c7af72</dt><dd>{source.next_sync_at && source.sync_enabled ? dateTimeLabel(new Date(source.next_sync_at)) : "i18n:govoplan-calendar.not_scheduled.9c367369"}</dd></div>
<div><dt>i18n:govoplan-calendar.credential.8bede3ea</dt><dd>{source.has_credential || source.credential_ref ? "i18n:govoplan-calendar.configured.668c5fff" : "i18n:govoplan-calendar.not_configured.811931bb"}</dd></div> <div><dt>i18n:govoplan-calendar.credential.8bede3ea</dt><dd>{source.has_credential ? "i18n:govoplan-calendar.configured.668c5fff" : "i18n:govoplan-calendar.not_configured.811931bb"}</dd></div>
</dl> </dl>
{source.last_error && <p className="calendar-form-error">{source.last_error}</p>} {source.last_error && <p className="calendar-form-error">{source.last_error}</p>}
<div className="calendar-sync-actions"> <div className="calendar-sync-actions">
@@ -1704,7 +1696,7 @@ function CalendarCollectionDialog({
</div> </div>
</div> </div>
} }
{sourceMode !== "local" && effectiveAuthType !== "none" && !source?.has_credential && !credentialRef.trim() && <p className="calendar-form-note">i18n:govoplan-calendar.enter_a_secret_now_or_use_an_environment_referen.6db0e6ce <code>env:CALENDAR_SYNC_SECRET</code>.</p>} {needsSourceSecret && <p className="calendar-form-note">i18n:govoplan-calendar.enter_a_password_or_token_for_this_source.74c09a54</p>}
</section> </section>
} }
</form> </form>
@@ -1812,10 +1804,7 @@ function CalendarCollectionDeleteDialog({
</select> </select>
</label> </label>
{calendar.is_default && {calendar.is_default &&
<label className="calendar-checkbox-row"> <ToggleSwitch label="i18n:govoplan-calendar.make_target_calendar_the_default.10a3977b" checked={makeTargetDefault} onChange={setMakeTargetDefault} />
<input type="checkbox" checked={makeTargetDefault} onChange={(item) => setMakeTargetDefault(item.target.checked)} />
<span>i18n:govoplan-calendar.make_target_calendar_the_default.10a3977b</span>
</label>
} }
<p className="calendar-form-note">{calendarBulkMoveConsequence(externalAction)}</p> <p className="calendar-form-note">{calendarBulkMoveConsequence(externalAction)}</p>
</> </>
@@ -2420,7 +2409,6 @@ function syncSourceCreatePayload(sourceMode: CalendarSourceMode, payload: Calend
display_name: payload.display_name.trim() || null, display_name: payload.display_name.trim() || null,
auth_type: sourceKind === "graph" ? "bearer" : payload.auth_type, auth_type: sourceKind === "graph" ? "bearer" : payload.auth_type,
username: sourceKind !== "graph" && payload.auth_type === "basic" ? payload.username.trim() || null : null, username: sourceKind !== "graph" && payload.auth_type === "basic" ? payload.username.trim() || null : null,
credential_ref: payload.credential_ref.trim() || null,
sync_enabled: payload.sync_enabled, sync_enabled: payload.sync_enabled,
sync_interval_seconds: Math.max(60, payload.sync_interval_seconds), sync_interval_seconds: Math.max(60, payload.sync_interval_seconds),
sync_direction: sourceKind === "caldav" ? payload.sync_direction : "inbound", sync_direction: sourceKind === "caldav" ? payload.sync_direction : "inbound",
@@ -2436,8 +2424,7 @@ function syncSourceConnectionUpdatePayload(payload: CalendarCalDavFormPayload):
const result: CalendarSyncSourceUpdatePayload = { const result: CalendarSyncSourceUpdatePayload = {
collection_url: payload.collection_url.trim(), collection_url: payload.collection_url.trim(),
auth_type: payload.auth_type, auth_type: payload.auth_type,
username: payload.auth_type === "basic" ? payload.username.trim() || null : null, username: payload.auth_type === "basic" ? payload.username.trim() || null : null
credential_ref: payload.credential_ref.trim() || null
}; };
if (payload.auth_type === "basic" && payload.password.trim()) result.password = payload.password; if (payload.auth_type === "basic" && payload.password.trim()) result.password = payload.password;
if (payload.auth_type === "bearer" && payload.bearer_token.trim()) result.bearer_token = payload.bearer_token; if (payload.auth_type === "bearer" && payload.bearer_token.trim()) result.bearer_token = payload.bearer_token;

View File

@@ -39,7 +39,6 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-calendar.confirmed.0542404a": "CONFIRMED", "i18n:govoplan-calendar.confirmed.0542404a": "CONFIRMED",
"i18n:govoplan-calendar.conflict_policy.5810e150": "Conflict policy", "i18n:govoplan-calendar.conflict_policy.5810e150": "Conflict policy",
"i18n:govoplan-calendar.continuous.04f2ccda": "Continuous", "i18n:govoplan-calendar.continuous.04f2ccda": "Continuous",
"i18n:govoplan-calendar.credential_reference.a7e92de5": "Credential reference",
"i18n:govoplan-calendar.credential.8bede3ea": "Credential", "i18n:govoplan-calendar.credential.8bede3ea": "Credential",
"i18n:govoplan-calendar.dav_url.4205e180": "DAV URL", "i18n:govoplan-calendar.dav_url.4205e180": "DAV URL",
"i18n:govoplan-calendar.day.987b9ced": "Day", "i18n:govoplan-calendar.day.987b9ced": "Day",
@@ -63,7 +62,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-calendar.end_date.89d10cd6": "End date", "i18n:govoplan-calendar.end_date.89d10cd6": "End date",
"i18n:govoplan-calendar.end_mode.5a06de37": "End mode", "i18n:govoplan-calendar.end_mode.5a06de37": "End mode",
"i18n:govoplan-calendar.end_time.cd7800da": "End time", "i18n:govoplan-calendar.end_time.cd7800da": "End time",
"i18n:govoplan-calendar.enter_a_secret_now_or_use_an_environment_referen.6db0e6ce": "Enter a secret now or use an environment reference such as", "i18n:govoplan-calendar.enter_a_password_or_token_for_this_source.74c09a54": "Enter a password or token for this source.",
"i18n:govoplan-calendar.etag.11d00f6e": "ETag", "i18n:govoplan-calendar.etag.11d00f6e": "ETag",
"i18n:govoplan-calendar.event": "event", "i18n:govoplan-calendar.event": "event",
"i18n:govoplan-calendar.event_count_could_not_be_loaded_the_backend_will.163ff055": "Event count could not be loaded. The backend will still apply the selected delete action.", "i18n:govoplan-calendar.event_count_could_not_be_loaded_the_backend_will.163ff055": "Event count could not be loaded. The backend will still apply the selected delete action.",
@@ -228,7 +227,6 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-calendar.confirmed.0542404a": "CONFIRMED", "i18n:govoplan-calendar.confirmed.0542404a": "CONFIRMED",
"i18n:govoplan-calendar.conflict_policy.5810e150": "Conflict policy", "i18n:govoplan-calendar.conflict_policy.5810e150": "Conflict policy",
"i18n:govoplan-calendar.continuous.04f2ccda": "Continuous", "i18n:govoplan-calendar.continuous.04f2ccda": "Continuous",
"i18n:govoplan-calendar.credential_reference.a7e92de5": "Credential reference",
"i18n:govoplan-calendar.credential.8bede3ea": "Credential", "i18n:govoplan-calendar.credential.8bede3ea": "Credential",
"i18n:govoplan-calendar.dav_url.4205e180": "DAV URL", "i18n:govoplan-calendar.dav_url.4205e180": "DAV URL",
"i18n:govoplan-calendar.day.987b9ced": "Tag", "i18n:govoplan-calendar.day.987b9ced": "Tag",
@@ -252,7 +250,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-calendar.end_date.89d10cd6": "End date", "i18n:govoplan-calendar.end_date.89d10cd6": "End date",
"i18n:govoplan-calendar.end_mode.5a06de37": "End mode", "i18n:govoplan-calendar.end_mode.5a06de37": "End mode",
"i18n:govoplan-calendar.end_time.cd7800da": "End time", "i18n:govoplan-calendar.end_time.cd7800da": "End time",
"i18n:govoplan-calendar.enter_a_secret_now_or_use_an_environment_referen.6db0e6ce": "Enter a secret now or use an environment reference such as", "i18n:govoplan-calendar.enter_a_password_or_token_for_this_source.74c09a54": "Geben Sie ein Passwort oder Token für diese Quelle ein.",
"i18n:govoplan-calendar.etag.11d00f6e": "ETag", "i18n:govoplan-calendar.etag.11d00f6e": "ETag",
"i18n:govoplan-calendar.event": "event", "i18n:govoplan-calendar.event": "event",
"i18n:govoplan-calendar.event_count_could_not_be_loaded_the_backend_will.163ff055": "Event count could not be loaded. The backend will still apply the selected delete action.", "i18n:govoplan-calendar.event_count_could_not_be_loaded_the_backend_will.163ff055": "Event count could not be loaded. The backend will still apply the selected delete action.",