I would like anonymous users to be able to only read and write their own data. I have the below as my security rules, but am getting a cannot read error in the simulator and the app.
I’m not sure that I’m going about it the right way. My main objective is to nest new assessments of the same user under their uid’s and make it so they can only read, write and update their own assessments.
{ "rules": { "users": { "$uid": { ".write": "$uid === auth.uid"; ".read": "$uid === auth.uid"; } } } }
This is what my branch currently looks like
This is what I think it should look like to accomplish what I need. Ideal Database structure
auth.gaurd.ts
import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; import { AuthService } from "../../shared/service/auth.service"; import { Observable } from 'rxjs'; import * as firebase from 'firebase'; @Injectable({ providedIn: 'root' }) export class AuthGuard implements CanActivate { uid: string; constructor( public authService: AuthService, public router: Router ){ } canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean { this.authStateListener(); return true; } authStateListener() { // [START auth_state_listener] firebase.auth().onAuthStateChanged((user) => { if (user) { // User is signed in, see docs for a list of available properties this.uid = user.uid; console.log("user"+user.isAnonymous) console.log("uid"+this.uid) } else { // User is signed out return firebase.auth().signOut().then(() => { localStorage.removeItem('user'); this.router.navigate(['sign-in']); }) } }); } }
auth.service.ts
import { Injectable, NgZone, ViewChild, ElementRef, Component } from '@angular/core'; import { User } from "../service/user"; import { auth } from 'firebase/app'; import { AngularFireAuth } from "@angular/fire/auth"; import { AngularFirestore, AngularFirestoreDocument } from '@angular/fire/firestore'; import { ActivatedRoute, Router } from "@angular/router"; import * as firebase from 'firebase'; import "firebase/auth"; @Injectable({ providedIn: 'root' }) export class AuthService { userData: any; // Save logged in user data @ViewChild('btnLogin') btnLogin: HTMLButtonElement; constructor( public afs: AngularFirestore, // Inject Firestore service public afAuth: AngularFireAuth, // Inject Firebase auth service public router: Router, private actRoute: ActivatedRoute, public ngZone: NgZone // NgZone service to remove outside scope warning ) { } anonymousSignIn(){ firebase.auth().signInAnonymously() .then(()=>{ this.router.navigate(['assessment']); console.log("clicking") }).catch((error) => { var errorCode = error.code; var errorMessage = error.message; console.log("error here") }); }
**This is the code to push, read, update and delete branches in Firebase. The ReadAssessment list should display all data that the anonymous user owns in order for them to read it. ** fire-assessment.service.ts
import { AuthGuard } from './../shared/guard/auth.guard'; import { Injectable } from '@angular/core'; import {AngularFireDatabase, AngularFireList, AngularFireObject} from '@angular/fire/database'; import * as firebase from 'firebase'; import {Business} from '../models/business'; import { ActivatedRoute, Router } from '@angular/router'; import { map } from 'rxjs/internal/operators/map'; import { isNgTemplate } from '@angular/compiler'; @Injectable({ providedIn: 'root' }) export class FireAssessmentService { assessmentsRef: AngularFireList<any>; // Reference to Assessment data list, its an Observable assessmentRef: AngularFireObject<any>; // Reference to assessment object public database = firebase.database(); public UserAssessmentInput; public ref; public actRoute: ActivatedRoute; public router: Router; public auth: AuthGuard; constructor(private db: AngularFireDatabase) { } CreateAssessment(business: Business ){ const key = this.database.ref('/users').push().key; this.database.ref('/users').child(key).set( ///this.assessmentsRef.ref('/users').push( { busiName: business.busiName }); } ReadAssessment(id: string){ this.assessmentRef = this.db.object('users/' + id); return this.assessmentRef; } ReadAssessmentsList(){ this.assessmentsRef = this.db.list('users/'); return this.assessmentsRef; } UpdateAssessments (business: Business){ this.assessmentRef.update({ busiName: business.busiName }); } DeleteAssessment(){ this.assessmentRef = this.db.object('users/'); this.assessmentRef.remove(); }
business.ts
export interface Business { $key: string; busiName: string; }
Advertisement
Answer
Right now you’re creating data with this:
const key = this.database.ref('/users').push().key; this.database.ref('/users').child(key).set({ busiName: business.busiName });
When you call push()
Firebase generates a new unique location, which is the key starting with -M...
in your JSON.
That value is not the UID of the current user, so these rules then don’t allow the user to read or write it:
"users": { "$uid": { ".write": "$uid === auth.uid"; ".read": "$uid === auth.uid"; } }
Instead you should write the data under a node using the user’s UID as the key. That’d look something like:
const key = this.database.ref('/users').push().key; if (firebase.auth().currentUser) { const key = firebase.auth().currentUser.uid; this.database.ref('/users').child(key).set({ busiName: business.busiName }); } else { console.error("No current user while trying to write business name"); }