diff --git a/firestore/firestore.rules b/firestore/firestore.rules index a0e3aae86..c3741c0f7 100644 --- a/firestore/firestore.rules +++ b/firestore/firestore.rules @@ -103,11 +103,17 @@ } /** - * Returns true iff the user with the provided email created the specified resource. + * Returns true iff the current user is the owner of the specified LOI. */ - function isCreator(resource) { - // TODO(#1154): Make `owner` its own field and stop assuming `creator==owner`. - return resource.data.created.user.email == request.auth.token.email; + function isLoiOwner(loi) { + return loi.data['5'] == request.auth.uid; + } + + /** + * Returns true iff the current user is the owner of the specified submission. + */ + function isSubmissionOwner(submission) { + return submission.data['5'] == request.auth.uid; } // Allow users to read, create, and write their own profiles in the db. @@ -138,10 +144,9 @@ // Allow if user has has read access to the survey. allow read: if canViewSurvey(getSurvey(surveyId)); // Allow if user is owner of the new LOI and can collect data. - // TODO(#1154): Require owner as well. - allow create: if canCollectData(getSurvey(surveyId)); + allow create: if isLoiOwner(request.resource) && canCollectData(getSurvey(surveyId)); // Allow if user is owner of the existing LOI or can manage survey. - allow write: if isCreator(resource) || canManageSurvey(getSurvey(surveyId)); + allow write: if isLoiOwner(resource) || canManageSurvey(getSurvey(surveyId)); } // Apply passlist and survey-level ACLs to submission documents. @@ -149,9 +154,9 @@ // Allow if user has has read access to the survey. allow read: if canViewSurvey(getSurvey(surveyId)); // Allow if user is owner of the new submission and can collect data. - allow create: if isCreator(request.resource) && canCollectData(getSurvey(surveyId)); + allow create: if isSubmissionOwner(request.resource) && canCollectData(getSurvey(surveyId)); // Allow if user is owner of the existing submission or can manage survey. - allow write: if isCreator(resource) || canManageSurvey(getSurvey(surveyId)); + allow write: if isSubmissionOwner(resource) || canManageSurvey(getSurvey(surveyId)); } // Apply passlist and survey-level ACLs to job documents. diff --git a/functions/src/import-geojson.ts b/functions/src/import-geojson.ts index a06628891..528802710 100644 --- a/functions/src/import-geojson.ts +++ b/functions/src/import-geojson.ts @@ -59,6 +59,8 @@ export function importGeoJsonCallback( const db = getDatastore(); + const ownerId = user.uid; + // This code will process each file uploaded. busboy.on('file', async (_field, file, _filename) => { const {survey: surveyId, job: jobId} = params; @@ -101,7 +103,9 @@ export function importGeoJsonCallback( return; } try { - const data = toDocumentData(toLoiPb(geoJsonLoi as Feature, jobId)); + const data = toDocumentData( + toLoiPb(geoJsonLoi as Feature, jobId, ownerId) + ); const loi = { ...data, ...geoJsonToLoiLegacy(geoJsonLoi, jobId), @@ -169,12 +173,17 @@ function geoJsonToLoiLegacy(geoJsonLoi: Feature, jobId: string): DocumentData { * Convert the provided GeoJSON LocationOfInterest and jobId into a * LocationOfInterest for insertion into the data store. */ -function toLoiPb(feature: Feature, jobId: string): Pb.LocationOfInterest { +function toLoiPb( + feature: Feature, + jobId: string, + ownerId: string +): Pb.LocationOfInterest { // TODO: Add created/modified metadata. const {id, geometry, properties} = feature; const geometryPb = toGeometryPb(geometry); return new Pb.LocationOfInterest({ jobId, + ownerId, customTag: id?.toString(), source: Pb.LocationOfInterest.Source.IMPORTED, geometry: geometryPb,