forked from quadratic-funding/mpc-phase2-suite
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(security rules): implemented Firestore security rules
Implemented security rules to protect data in the Firestore db. This includes test cases to verify the correctness of the security rules. fix quadratic-funding#28
- Loading branch information
Showing
4 changed files
with
133 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,6 +35,20 @@ const fakeUser2 = generateFakeUser({ | |
} | ||
}) | ||
|
||
const fakeUser3 = generateFakeUser({ | ||
uid: "0000000000000000000000000003", | ||
data: { | ||
name: "user3", | ||
displayName: undefined, | ||
creationTime: Date.now(), | ||
lastSignInTime: Date.now() + 1, | ||
lastUpdated: Date.now() + 2, | ||
email: "[email protected]", | ||
emailVerified: false, | ||
photoURL: undefined | ||
} | ||
}) | ||
|
||
const fakeCeremonyScheduledFixed = generateFakeCeremony({ | ||
uid: "0000000000000000000A", | ||
data: { | ||
|
@@ -257,7 +271,8 @@ const fakeCircuitSmallContributors = generateFakeCircuit({ | |
|
||
export const fakeUsersData = { | ||
fakeUser1, | ||
fakeUser2 | ||
fakeUser2, | ||
fakeUser3 | ||
} | ||
|
||
export const fakeCeremoniesData = { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import chai, { expect } from "chai" | ||
import chaiAsPromised from "chai-as-promised" | ||
import { getAuth, signInWithEmailAndPassword } from "firebase/auth" | ||
import { | ||
createNewFirebaseUserWithEmailAndPw, | ||
deleteAdminApp, | ||
generatePseudoRandomStringOfNumbers, | ||
initializeAdminServices, | ||
initializeUserServices, | ||
sleep, | ||
addCoordinatorPrivileges | ||
} from "../utils" | ||
import { fakeUsersData } from "../data/samples" | ||
import { getCurrentFirebaseAuthUser } from "../../src" | ||
import { getDocumentById } from "../../src/helpers/query" | ||
|
||
chai.use(chaiAsPromised) | ||
|
||
describe("Security rules", () => { | ||
// Init admin services. | ||
const { adminFirestore, adminAuth } = initializeAdminServices() | ||
const { userApp, userFirestore } = initializeUserServices() | ||
const userAuth = getAuth(userApp) | ||
|
||
const user1 = fakeUsersData.fakeUser1 | ||
const user2 = fakeUsersData.fakeUser2 | ||
const user3 = fakeUsersData.fakeUser3 | ||
const user1Pwd = generatePseudoRandomStringOfNumbers(24) | ||
const user2Pwd = generatePseudoRandomStringOfNumbers(24) | ||
const user3Pwd = generatePseudoRandomStringOfNumbers(24) | ||
|
||
beforeAll(async () => { | ||
// create 1st user | ||
await createNewFirebaseUserWithEmailAndPw(userApp, user1.data.email, user1Pwd) | ||
|
||
// Retrieve the current auth user in Firebase. | ||
let currentAuthenticatedUser = getCurrentFirebaseAuthUser(userApp) | ||
user1.uid = currentAuthenticatedUser.uid | ||
|
||
// create 2nd user | ||
await createNewFirebaseUserWithEmailAndPw(userApp, user2.data.email, user2Pwd) | ||
|
||
// Retrieve the current auth user in Firebase. | ||
currentAuthenticatedUser = getCurrentFirebaseAuthUser(userApp) | ||
user2.uid = currentAuthenticatedUser.uid | ||
await sleep(5000) // 5s delay. | ||
|
||
// create the coordinator | ||
await createNewFirebaseUserWithEmailAndPw(userApp, user3.data.email, user3Pwd) | ||
currentAuthenticatedUser = getCurrentFirebaseAuthUser(userApp) | ||
user3.uid = currentAuthenticatedUser.uid | ||
await sleep(5000) // 5s delay. | ||
}) | ||
|
||
it("should work as expected and return the data for the same user", async () => { | ||
// login as user1 | ||
await signInWithEmailAndPassword(userAuth, user1.data.email, user1Pwd) | ||
const userDoc = await getDocumentById(userFirestore, "users", user1.uid) | ||
const data = userDoc.data() | ||
expect(data).to.not.be.null | ||
}) | ||
|
||
it("should not return another user's document", async () => { | ||
// login as user2 | ||
await signInWithEmailAndPassword(userAuth, user2.data.email, user2Pwd) | ||
// below should fail because we are trying to retrieve a document from another user. | ||
expect(getDocumentById(userFirestore, "users", user1.uid)).to.be.rejectedWith( | ||
"Missing or insufficient permissions." | ||
) | ||
}) | ||
|
||
it("should allow the coordinator to read another user's document", async () => { | ||
// login as user3 | ||
await signInWithEmailAndPassword(userAuth, user3.data.email, user3Pwd) | ||
// Retrieve the current auth user in Firebase. | ||
const currentAuthenticatedUser = getCurrentFirebaseAuthUser(userApp) | ||
// sleep | ||
await sleep(5000) | ||
// add coordinator privileges | ||
await addCoordinatorPrivileges(adminAuth, user3.uid) | ||
// force refresh | ||
await currentAuthenticatedUser.getIdTokenResult(true) | ||
// retrieve the document of another user | ||
const userDoc = await getDocumentById(userFirestore, "users", user1.uid) | ||
const data = userDoc.data() | ||
expect(data).to.not.be.null | ||
}) | ||
afterAll(async () => { | ||
// Clean user from DB. | ||
await adminFirestore.collection("users").doc(user1.uid).delete() | ||
await adminFirestore.collection("users").doc(user2.uid).delete() | ||
await adminFirestore.collection("users").doc(user3.uid).delete() | ||
// Remove Auth user. | ||
await adminAuth.deleteUser(user1.uid) | ||
await adminAuth.deleteUser(user2.uid) | ||
await adminAuth.deleteUser(user3.uid) | ||
// Delete admin app. | ||
await deleteAdminApp() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,22 @@ | ||
rules_version = '2'; | ||
// Allow read/write access on all documents to any user signed in to the application | ||
service cloud.firestore { | ||
match /databases/{database}/documents { | ||
match /{document=**} { | ||
allow read, write: if request.auth != null; | ||
// Define which users can read and write to the database | ||
match /users/{userId} { | ||
// users can read update and delete their own data and coordinator can read, update, delete any user's data | ||
allow read, update, delete: | ||
if request.auth != null && | ||
request.auth.uid == userId || | ||
request.auth.token.coordinator; | ||
// coordinator can create new users | ||
allow create: | ||
if request.auth != null && request.auth.token.coordinator; | ||
} | ||
match /ceremonies/{ceremonyId} { | ||
// any authenticated user can read | ||
allow read: if request.auth != null; | ||
// only coordinator can create, update, delete ceremonies | ||
allow create, update, delete: if request.auth != null && request.auth.token.coordinator; | ||
} | ||
} | ||
} |