rules_version = '2'; // To pick the right project: `firebase projects:list`, then `firebase use ` // To deploy: `firebase deploy --only firestore:rules` service cloud.firestore { match /databases/{database}/documents { function isAdmin() { return request.auth.token.email in [ 'akrolsmir@gmail.com', 'jahooma@gmail.com', 'taowell@gmail.com', 'abc.sinclair@gmail.com', 'manticmarkets@gmail.com', 'iansphilips@gmail.com', 'd4vidchee@gmail.com', 'federicoruizcassarino@gmail.com', 'ingawei@gmail.com' ] } match /stats/stats { allow read; } match /globalConfig/globalConfig { allow read; allow update: if isAdmin() allow create: if isAdmin() } match /users/{userId} { allow read; allow update: if userId == request.auth.uid && request.resource.data.diff(resource.data).affectedKeys() .hasOnly(['bio', 'website', 'twitterHandle', 'discordHandle', 'followedCategories', 'lastPingTime','shouldShowWelcome', 'hasSeenContractFollowModal', 'homeSections']); // User referral rules allow update: if userId == request.auth.uid && request.resource.data.diff(resource.data).affectedKeys() .hasOnly(['referredByUserId', 'referredByContractId', 'referredByGroupId']) // only one referral allowed per user && !("referredByUserId" in resource.data) // user can't refer themselves && !(userId == request.resource.data.referredByUserId); // quid pro quos enabled (only once though so nbd) - bc I can't make this work: // && (get(/databases/$(database)/documents/users/$(request.resource.data.referredByUserId)).referredByUserId == resource.data.id); } match /{somePath=**}/portfolioHistory/{portfolioHistoryId} { allow read; } match /{somePath=**}/contract-metrics/{contractId} { allow read; } match /{somePath=**}/challenges/{challengeId}{ allow read; } match /contracts/{contractId}/follows/{userId} { allow read; allow create, delete: if userId == request.auth.uid; } match /contracts/{contractId}/challenges/{challengeId}{ allow read; allow create: if request.auth.uid == request.resource.data.creatorId; // allow update if there have been no claims yet and if the challenge is still open allow update: if request.auth.uid == resource.data.creatorId; } match /users/{userId}/follows/{followUserId} { allow read; allow write: if request.auth.uid == userId; } match /users/{userId}/likes/{likeId} { allow read; allow write: if request.auth.uid == userId; } match /{somePath=**}/follows/{followUserId} { allow read; } match /private-users/{userId} { allow read: if userId == request.auth.uid || isAdmin(); allow update: if (userId == request.auth.uid || isAdmin()) && request.resource.data.diff(resource.data).affectedKeys() .hasOnly(['apiKey', 'notificationPreferences', 'twitchInfo']); } 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', 'groupSlugs', 'groupLinks', 'flaggedByUsernames']); allow update: if request.resource.data.diff(resource.data).affectedKeys() .hasOnly(['description', 'closeTime', 'question', 'visibility', 'unlistedById']) && 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 /{somePath=**}/groupMembers/{memberId} { allow read; } match /{somePath=**}/groupContracts/{contractId} { allow read; } match /groups/{groupId} { allow read; allow update: if (request.auth.uid == resource.data.creatorId || isAdmin()) && request.resource.data.diff(resource.data) .affectedKeys() .hasOnly(['name', 'about', 'anyoneCanJoin', 'aboutPostId', 'pinnedItems' ]); allow delete: if request.auth.uid == resource.data.creatorId; match /groupContracts/{contractId} { allow write: if isGroupMember() || request.auth.uid == get(/databases/$(database)/documents/groups/$(groupId)).data.creatorId } match /groupMembers/{memberId}{ allow create: if request.auth.uid == get(/databases/$(database)/documents/groups/$(groupId)).data.creatorId || (request.auth.uid == request.resource.data.userId && get(/databases/$(database)/documents/groups/$(groupId)).data.anyoneCanJoin); allow delete: if request.auth.uid == resource.data.userId; } function isGroupMember() { return exists(/databases/$(database)/documents/groups/$(groupId)/groupMembers/$(request.auth.uid)); } match /comments/{commentId} { allow read; allow create: if request.auth != null && commentMatchesUser(request.auth.uid, request.resource.data) && isGroupMember(); } } match /posts/{postId} { allow read; allow update: if isAdmin() || request.auth.uid == resource.data.creatorId && request.resource.data.diff(resource.data) .affectedKeys() .hasOnly(['name', 'content']); allow delete: if isAdmin() || request.auth.uid == resource.data.creatorId; match /comments/{commentId} { allow read; allow create: if request.auth != null && commentMatchesUser(request.auth.uid, request.resource.data) ; } } } }