6cc2d8af58
* Set up Firestore structure for mana bounty links
* Split up manalinks into successes and failures
* Allow clients to create manalinks
* Track txnId and successful users
* Store custom amounts in the link
* List all manalinks you've created
* Support backend for claiming manalinks
* Add some more error handling
* Tweak readme
* Fix typescript breakage
* Revert "Convert common imports in functions to be absolute"
This reverts commit c03518e906
.
* Scaffolding so `claimManalink` works
* Clean up imports
* Barebones endpoint to claim mana
* Fix rules to only allow link creators to query
* Design out claim giftcard
* List all claimed transactions
* Style in a more awesome card
* Fix import
* Padding tweak
* Fix useManalinkTxns hook
* /send -> /link
* Tidy up some details
* Do a bunch of random manalinks work
* Fix up LinksTable to build
* Clean up LinksTable an absurd amount
* Basic details functionality on manalinks table
* Work on manalink claim stuff
* Fix up some merge mess
* Not-signed-in flow implemented
* Better manalinks table
* Only show outstanding links in table
* Use new `ManalinkTxn` type
* /link -> /links
* Change manalinks page UI to use nice looking tabs
* Many fixes to manalinks UI
* Default to 1 use
* Tidying up
* Some copy changes based on feedback
* Add required index
Co-authored-by: Marshall Polaris <marshall@pol.rs>
146 lines
5.5 KiB
Plaintext
146 lines
5.5 KiB
Plaintext
rules_version = '2';
|
|
|
|
// To pick the right project: `firebase projects:list`, then `firebase use <project-name>`
|
|
// To deploy: `firebase deploy --only firestore:rules`
|
|
service cloud.firestore {
|
|
match /databases/{database}/documents {
|
|
|
|
function isAdmin() {
|
|
return request.auth.uid == 'igi2zGXsfxYPgB0DJTXVJVmwCOr2' // Austin
|
|
|| request.auth.uid == '5LZ4LgYuySdL1huCWe7bti02ghx2' // James
|
|
|| request.auth.uid == 'tlmGNz9kjXc2EteizMORes4qvWl2' // Stephen
|
|
|| request.auth.uid == 'IPTOzEqrpkWmEzh6hwvAyY9PqFb2' // Manifold
|
|
}
|
|
|
|
match /users/{userId} {
|
|
allow read;
|
|
allow update: if resource.data.id == request.auth.uid
|
|
&& request.resource.data.diff(resource.data).affectedKeys()
|
|
.hasOnly(['bio', 'bannerUrl', 'website', 'twitterHandle', 'discordHandle', 'followedCategories']);
|
|
}
|
|
|
|
match /{somePath=**}/portfolioHistory/{portfolioHistoryId} {
|
|
allow read;
|
|
}
|
|
|
|
match /users/{userId}/follows/{followUserId} {
|
|
allow read;
|
|
allow write: if request.auth.uid == userId;
|
|
}
|
|
|
|
match /{somePath=**}/follows/{followUserId} {
|
|
allow read;
|
|
}
|
|
|
|
match /private-users/{userId} {
|
|
allow read: if resource.data.id == request.auth.uid || isAdmin();
|
|
allow update: if (resource.data.id == request.auth.uid || isAdmin())
|
|
&& request.resource.data.diff(resource.data).affectedKeys()
|
|
.hasOnly(['apiKey', 'unsubscribedFromResolutionEmails', 'unsubscribedFromCommentEmails', 'unsubscribedFromAnswerEmails', 'notificationPreferences' ]);
|
|
}
|
|
|
|
match /private-users/{userId}/views/{viewId} {
|
|
allow create: if userId == request.auth.uid;
|
|
}
|
|
|
|
match /private-users/{userId}/events/{eventId} {
|
|
allow create: if userId == request.auth.uid;
|
|
}
|
|
|
|
match /private-users/{userId}/latency/{loadTimeId} {
|
|
allow create: if userId == request.auth.uid;
|
|
}
|
|
|
|
match /private-users/{userId}/cache/{docId} {
|
|
allow read: if userId == request.auth.uid || isAdmin();
|
|
}
|
|
|
|
match /contracts/{contractId} {
|
|
allow read;
|
|
allow update: if request.resource.data.diff(resource.data).affectedKeys()
|
|
.hasOnly(['tags', 'lowercaseTags']);
|
|
allow update: if request.resource.data.diff(resource.data).affectedKeys()
|
|
.hasOnly(['description', 'closeTime'])
|
|
&& resource.data.creatorId == request.auth.uid;
|
|
allow update: if isAdmin();
|
|
match /comments/{commentId} {
|
|
allow create: if request.auth != null && commentMatchesUser(request.auth.uid, request.resource.data);
|
|
}
|
|
}
|
|
|
|
match /{somePath=**}/bets/{betId} {
|
|
allow read;
|
|
}
|
|
|
|
match /{somePath=**}/liquidity/{liquidityId} {
|
|
allow read;
|
|
}
|
|
|
|
function commentMatchesUser(userId, comment) {
|
|
// it's a bad look if someone can impersonate other ids/names/avatars so check everything
|
|
let user = get(/databases/$(database)/documents/users/$(userId));
|
|
return comment.userId == userId
|
|
&& comment.userName == user.data.name
|
|
&& comment.userUsername == user.data.username
|
|
&& comment.userAvatarUrl == user.data.avatarUrl;
|
|
}
|
|
|
|
match /{somePath=**}/comments/{commentId} {
|
|
allow read;
|
|
}
|
|
|
|
match /{somePath=**}/answers/{answerId} {
|
|
allow read;
|
|
}
|
|
|
|
|
|
match /{somePath=**}/followers/{userId} {
|
|
allow read;
|
|
allow create, update: if request.auth.uid == userId && request.resource.data.userId == userId;
|
|
allow delete: if request.auth.uid == userId;
|
|
}
|
|
|
|
match /txns/{txnId} {
|
|
allow read;
|
|
}
|
|
|
|
// Note: `resource` = existing doc, `request.resource` = incoming doc
|
|
match /manalinks/{slug} {
|
|
// Anyone can view any manalink
|
|
allow get;
|
|
// Only you can create a manalink with your fromId
|
|
allow create: if request.auth.uid == request.resource.data.fromId;
|
|
// Only you can list and change your own manalinks
|
|
allow list, update: if request.auth.uid == resource.data.fromId;
|
|
}
|
|
|
|
match /users/{userId}/notifications/{notificationId} {
|
|
allow read;
|
|
allow update: if resource.data.userId == request.auth.uid
|
|
&& request.resource.data.diff(resource.data).affectedKeys()
|
|
.hasOnly(['isSeen', 'viewTime']);
|
|
}
|
|
|
|
match /groups/{groupId} {
|
|
allow read;
|
|
allow update: if request.auth.uid == resource.data.creatorId
|
|
&& request.resource.data.diff(resource.data)
|
|
.affectedKeys()
|
|
.hasOnly(['name', 'about', 'contractIds', 'memberIds', 'anyoneCanJoin' ]);
|
|
allow update: if (request.auth.uid in resource.data.memberIds || resource.data.anyoneCanJoin)
|
|
&& request.resource.data.diff(resource.data)
|
|
.affectedKeys()
|
|
.hasOnly([ 'contractIds', 'memberIds' ]);
|
|
allow delete: if request.auth.uid == resource.data.creatorId;
|
|
|
|
function isMember() {
|
|
return request.auth.uid in get(/databases/$(database)/documents/groups/$(groupId)).data.memberIds;
|
|
}
|
|
match /comments/{commentId} {
|
|
allow read;
|
|
allow create: if request.auth != null && commentMatchesUser(request.auth.uid, request.resource.data) && isMember();
|
|
}
|
|
}
|
|
}
|
|
}
|