116 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			116 lines
		
	
	
		
			4.2 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 /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 /{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;
 | 
						|
      allow create: if request.auth != null && commentMatchesUser(request.auth.uid, request.resource.data);
 | 
						|
    }
 | 
						|
 | 
						|
    match /{somePath=**}/answers/{answerId} {
 | 
						|
      allow read;
 | 
						|
    }
 | 
						|
 | 
						|
    match /folds/{foldId} {
 | 
						|
      allow read;
 | 
						|
      allow update: if request.auth.uid == resource.data.curatorId
 | 
						|
        && request.resource.data.diff(resource.data).affectedKeys()
 | 
						|
        .hasOnly(['name', 'about', 'tags', 'lowercaseTags']);
 | 
						|
      allow delete: if request.auth.uid == resource.data.curatorId;
 | 
						|
    }
 | 
						|
 | 
						|
    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;
 | 
						|
    }
 | 
						|
 | 
						|
    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']);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 |