Skip to content

Commit

Permalink
Merge pull request #26 from PAIR-code/refactor-backend
Browse files Browse the repository at this point in the history
Refactor backend
  • Loading branch information
cjqian committed May 21, 2024
2 parents 144591f + f97092b commit 14a45ee
Show file tree
Hide file tree
Showing 123 changed files with 6,893 additions and 4,541 deletions.
2 changes: 1 addition & 1 deletion .firebaserc.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"projects": {
"default": "your-project-id-here"
"default": "your-project-id"
}
}
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"recommendations": [
"ethansk.restore-terminals" // Restore Terminals extension
"ethansk.restore-terminals", // Restore Terminals extension
"angular.ng-template"
]
}
13 changes: 12 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,16 @@
}
]
}
]
],
"editor.tabSize": 2,
"[html][typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
}
}
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ This is a repository to support collaboration on using LLMs in behavioral econom

## Shared Utilities

The webapp, cloud functions, and seeding scripts share some utilities. These are located in the [`utils`](./utils) directory.

To build the shared utilities and watch for changes, run the following command:

```bash
cd utils
npm run build:watch
```

The shared utilities are built using [`tsup`](https://tsup.egoist.dev) to produce both esm (for the webapp) and cjs (for the cloud functions and scripts) code.

## Firebase

This project uses Firebase as its backend. The configuration can be found in the [`.firebaserc`](./.firebaserc) and [`firebase.json`](./firebase.json) files.
Expand All @@ -75,6 +75,7 @@ Create the configuration files for a default firebase project:
```bash
cp .firebaserc.example .firebaserc
cp webapp/src/lib/api/firebase-config.example.ts webapp/src/lib/api/firebase-config.ts
cp scripts/service-account.example.json scripts/service-account.json
```

This should be enough for local development with emulators. Before deploying to production, be sure to:
Expand Down
1 change: 0 additions & 1 deletion emulator_test_config/auth_export/accounts.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"email": "experimenter@google.com"
}
],
"photoUrl": "",
"customAttributes": "{\"role\": \"experimenter\"}"
},
{
Expand Down
94 changes: 80 additions & 14 deletions firestore/firestore.rules
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,92 @@ rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {

// Rules for the messages collection
match /messages/{messageId} {
allow list: if request.auth.token.role == 'experimenter' || request.auth.token.role == 'participant';
// ***************************************************************************************** //
// VALIDATION FUNCTIONS //
// ***************************************************************************************** //

function validateString(value) {
return value is string;
}

function validateInt(value) {
return value is int
}

function validateFloat(value) {
return value is int || value is float;
}

// Rules for the participants_progressions collection
match /participants_progressions/{progressionId} {
// Allow single-document reads
allow read: if true;
function validateTimestamp(value) {
return value.keys().hasAll(['seconds', 'nanoseconds']) &&
validateNumber(value.seconds) &&
validateNumber(value.nanoseconds);
}

// Rules for the chat toggle collection
match /participants_ready_to_end_chat/{chatId} {
// Allow single document reads
allow read: if true;
// Validate the profile
function validateProfile(value) {
return value.keys().hasOnly(['pronouns', 'avatarUrl', 'name', 'acceptTosTimestamp']) &&
(!('pronouns' in value) || validateString(value.pronouns)) &&
(!('avatarUrl' in value) || validateString(value.avatarUrl)) &&
(!('name' in value) || validateString(value.name)) &&
(!('acceptTosTimestamp' in value) || validateTimestamp(value.acceptTosTimestamp));
}

// Else: disallow everything (access via cloud functions only)
match /{document=**} {
allow read, write: if false;
// Validate the chat document data
function validateChat(value) {
return value.keys().hasOnly(['readyToEndChat']) && value.readyToEndChat == false;
}

// ***************************************************************************************** //
// RULES //
// ***************************************************************************************** //

// Template rules (experimenter-only)
match /templates/{documents=**} {
allow read: if request.auth.token.role == 'experimenter';
allow write: if false; // Complex validation through cloud functions
}

// Experiment rules
match /experiments/{experimentId} {

allow get: if true;
allow list: if request.auth.token.role == 'experimenter';
allow delete: if request.auth.token.role == 'experimenter';
allow write: if false; // Complex validation through cloud functions

match /stages/{stageId} {
allow read: if true; // Public read
allow write: if false; // Write as experimenter through cloud functions
}

match /publicStageData/{stageId} {
allow read: if true;
allow write: if false; // Public readonly for everyone. Computed through firestore triggers
}

// Participant rules
match /participants/{participantId} {

allow get: if true; // Public if you know the ID
allow list: if request.auth.token.role == 'experimenter'; // Avoid leaking IDs (only experimenters can view them)
allow update: if true; // validateProfile(request.resource.data); // emulator bug

match /stages/{stageId} {
allow read: if true;
allow write: if false; // Complex validation through cloud functions
}

match /chats/{chatId} {
allow read: if true;
allow update: if true; // validateChat(request.resource.data); // emulator bug

match /messages/{messageId} {
allow read: if true;
allow write: if false; // Replication to all participants done through cloud functions in order to avoid trigger loops
}
}
}
}
}
}

0 comments on commit 14a45ee

Please sign in to comment.