added guardian login with views for their assigned childs

This commit is contained in:
2026-09-20 11:11:58 +02:00
parent e4e2b1a4fb
commit 67c935c05b
117 changed files with 4784 additions and 1514 deletions
+77
View File
@@ -0,0 +1,77 @@
import 'dart:convert';
/// Nextcloud identity of a session. Immutable; the session manager swaps in a
/// new instance whenever an app password is minted or revoked.
class NextcloudCredentials {
final String username;
/// The real account password. Invalid against Nextcloud when
/// [usesLoginFlow] is set (2FA accounts), where only [appPassword] works.
final String password;
final String? appPassword;
/// Backs the second (apptype=talk) push registration — Nextcloud binds each
/// push subscription to its session token, so two registrations need two
/// app passwords.
final String? appPasswordTalk;
/// True when the credentials came from Login Flow v2 (browser login, e.g.
/// because the account has two-factor authentication).
final bool usesLoginFlow;
const NextcloudCredentials({
required this.username,
required this.password,
this.appPassword,
this.appPasswordTalk,
this.usesLoginFlow = false,
});
bool get hasAppPassword => appPassword != null && appPassword!.isNotEmpty;
bool get hasAppPasswordTalk =>
appPasswordTalk != null && appPasswordTalk!.isNotEmpty;
/// The app password once available (minted or flow-issued), otherwise the
/// real password. It survives real-password rotation and is what the push
/// registration is bound to.
String get secret => hasAppPassword ? appPassword! : password;
/// HTTP Basic header value. Prefer headers over credentials in URLs — error
/// logs and crash reports often capture the URL but not headers.
String get basicAuthHeader => _basicAuth(secret);
Map<String, String> get authHeaders => {'Authorization': basicAuthHeader};
/// Authenticates the apptype=talk push registration (and its unregister).
/// Throws when the talk password has not been minted yet; callers treat that
/// as a failed talk registration and retry on the next start.
String get talkBasicAuthHeader {
if (hasAppPasswordTalk) return _basicAuth(appPasswordTalk!);
// Login-flow account whose second (talk) flow pass was skipped: no silent
// minting possible, the talk registration shares the flow credential.
if (usesLoginFlow && hasAppPassword) return _basicAuth(appPassword!);
throw StateError('Talk app password not available yet');
}
/// Always the real password. Needed to mint the app password via
/// `core/getapppassword` — an app password cannot mint another.
String get realPasswordBasicAuthHeader => _basicAuth(password);
NextcloudCredentials copyWith({
String? Function()? appPassword,
String? Function()? appPasswordTalk,
bool? usesLoginFlow,
}) => NextcloudCredentials(
username: username,
password: password,
appPassword: appPassword != null ? appPassword() : this.appPassword,
appPasswordTalk: appPasswordTalk != null
? appPasswordTalk()
: this.appPasswordTalk,
usesLoginFlow: usesLoginFlow ?? this.usesLoginFlow,
);
String _basicAuth(String secret) =>
'Basic ${base64Encode(utf8.encode('$username:$secret'))}';
}