72 lines
2.2 KiB
Plaintext
72 lines
2.2 KiB
Plaintext
rules_version = '2';
|
|
|
|
service firebase.storage {
|
|
match /b/{bucket}/o {
|
|
|
|
function isSignedIn() {
|
|
return request.auth != null;
|
|
}
|
|
|
|
// Single Firestore read — Storage rules allow at most 2 Firestore document accesses
|
|
// per evaluation; pairing exists()+get() on users then on upcoming/manga exceeds that.
|
|
function isAdmin() {
|
|
return isSignedIn()
|
|
&& firestore.get(/databases/(default)/documents/users/$(request.auth.uid)).data.role == 'admin';
|
|
}
|
|
|
|
function isReasonableImage() {
|
|
return request.resource != null
|
|
&& request.resource.size < 15 * 1024 * 1024
|
|
&& request.resource.contentType.matches('image/.*');
|
|
}
|
|
|
|
// fileName like "{upcomingDocId}.jpg" — Firestore row must exist; get() fails if missing.
|
|
function ownsUpcomingCover(fileName) {
|
|
return isSignedIn()
|
|
&& fileName.matches('.*\\.jpg')
|
|
&& fileName.split('.').size() == 2
|
|
&& firestore.get(/databases/(default)/documents/upcoming/$(fileName.split('.')[0])).data.authorId == request.auth.uid;
|
|
}
|
|
|
|
function mangaAuthorFromJpg(fileName) {
|
|
return isSignedIn()
|
|
&& fileName.split('.').size() == 2
|
|
&& fileName.split('.')[1] == 'jpg'
|
|
&& firestore.get(/databases/(default)/documents/manga/$(fileName.split('.')[0])).data.authorId == request.auth.uid;
|
|
}
|
|
|
|
match /{allPaths=**} {
|
|
allow read: if true;
|
|
}
|
|
|
|
match /avatars/{fileName} {
|
|
allow write: if isSignedIn()
|
|
&& isReasonableImage()
|
|
&& fileName == request.auth.uid + '.jpg';
|
|
}
|
|
|
|
match /social/{fileName} {
|
|
allow write: if isSignedIn() && isReasonableImage();
|
|
}
|
|
|
|
match /social_images/{fileName} {
|
|
allow write: if isSignedIn() && isReasonableImage();
|
|
}
|
|
|
|
match /series_covers/{fileName} {
|
|
allow write: if isReasonableImage()
|
|
&& (mangaAuthorFromJpg(fileName) || isAdmin());
|
|
}
|
|
|
|
match /series_banners/{fileName} {
|
|
allow write: if isReasonableImage()
|
|
&& (mangaAuthorFromJpg(fileName) || isAdmin());
|
|
}
|
|
|
|
match /upcoming_covers/{fileName} {
|
|
allow write: if isReasonableImage()
|
|
&& (ownsUpcomingCover(fileName) || isAdmin());
|
|
}
|
|
}
|
|
}
|