refactor(admin): remove hardcoded email and dynamically resolve admin privileges via Firestore Admins collection

This commit is contained in:
2026-09-14 11:44:04 +02:00
parent c3513e85e5
commit 877862aaf2
3 changed files with 65 additions and 15 deletions
+8 -1
View File
@@ -10,7 +10,8 @@ service cloud.firestore {
function isAdmin() { function isAdmin() {
return request.auth != null && ( return request.auth != null && (
request.auth.token.admin == true || request.auth.token.admin == true ||
request.auth.token.email == 'nicolas@gruber.info' exists(/databases/$(database)/documents/Admins/$(request.auth.uid)) ||
(request.auth.token.email != null && exists(/databases/$(database)/documents/Admins/$(request.auth.token.email)))
); );
} }
@@ -22,6 +23,12 @@ service cloud.firestore {
); );
} }
// Admins-Sammlung: Angemeldete Nutzer können prüfen, nur Admins schreiben
match /Admins/{adminDoc} {
allow read: if isAuthenticated();
allow write: if isAdmin();
}
// Öffentliche Sammlungen: News, Mängel (Issues) & Ideen (CitizensForum) // Öffentliche Sammlungen: News, Mängel (Issues) & Ideen (CitizensForum)
match /{collection}/{document=**} { match /{collection}/{document=**} {
// Jeder darf News, Mängel & Ideen öffentlich lesen // Jeder darf News, Mängel & Ideen öffentlich lesen
+55 -11
View File
@@ -1,5 +1,6 @@
import 'package:flutter/foundation.dart'; import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
class AuthService extends ChangeNotifier { class AuthService extends ChangeNotifier {
@@ -47,21 +48,64 @@ class AuthService extends ChangeNotifier {
_initAuth(); _initAuth();
} }
static const List<String> adminEmails = [
'nicolas@gruber.info',
];
Future<void> _checkAdminClaim(User? user) async { Future<void> _checkAdminClaim(User? user) async {
if (user != null && !user.isAnonymous) { if (user != null && !user.isAnonymous) {
final email = user.email?.trim().toLowerCase(); try {
if (email != null && adminEmails.contains(email)) { // 1. Custom claims check (token.admin == true)
final tokenResult = await user.getIdTokenResult();
if (tokenResult.claims?['admin'] == true) {
_isAdmin = true; _isAdmin = true;
return; return;
} }
try {
final tokenResult = await user.getIdTokenResult(); final firestore = FirebaseFirestore.instance;
_isAdmin = tokenResult.claims?['admin'] == true;
} catch (_) { // 2. Check Firestore 'Admins' collection by user UID (doc id)
final docByUid =
await firestore.collection('Admins').doc(user.uid).get();
if (docByUid.exists) {
_isAdmin = true;
return;
}
// 3. Check Firestore 'Admins' collection by email (doc id)
final userEmail = user.email?.trim().toLowerCase();
if (userEmail != null && userEmail.isNotEmpty) {
final docByEmail =
await firestore.collection('Admins').doc(userEmail).get();
if (docByEmail.exists) {
_isAdmin = true;
return;
}
// 4. Check Firestore 'Admins' collection where email field == userEmail
final queryByEmail = await firestore
.collection('Admins')
.where('email', isEqualTo: userEmail)
.limit(1)
.get();
if (queryByEmail.docs.isNotEmpty) {
_isAdmin = true;
return;
}
}
// 5. Check Firestore 'Admins' collection where uid field == user.uid
final queryByUid = await firestore
.collection('Admins')
.where('uid', isEqualTo: user.uid)
.limit(1)
.get();
if (queryByUid.docs.isNotEmpty) {
_isAdmin = true;
return;
}
_isAdmin = false;
} catch (e) {
if (kDebugMode) {
debugPrint('Error checking admin status: $e');
}
_isAdmin = false; _isAdmin = false;
} }
} else { } else {
+1 -2
View File
@@ -15,8 +15,7 @@ service firebase.storage {
// Nur der Verfasser oder ein Admin darf sein Foto löschen // Nur der Verfasser oder ein Admin darf sein Foto löschen
allow delete: if request.auth != null && ( allow delete: if request.auth != null && (
request.auth.uid == userId || request.auth.uid == userId ||
request.auth.token.admin == true || request.auth.token.admin == true
request.auth.token.email == 'nicolas@gruber.info'
); );
} }
} }