Swift: Firestore adding new data gives error - swift

Using Firestore, I'm trying to add a new collection and document. I keep getting "Missing or insufficient permissions". What's the problem? What permission do I still need?
struct FirestoreReferenceManager {
static let db = Firestore.firestore()
static let root = db.collection("dev").document("dev")
}
ViewController
#IBAction func handleRegistration(_ sender: Any) {
FirestoreReferenceManager.root.collection("cities").document("LA").setData(["name": "Los Angeles", "state": "CA"]) { (err) in
if let err = err {
print("Error writing document:", err.localizedDescription)
}
}
}

Try,
go to Database -> Rules -> Change allow read, write: if false to if request.auth != null
or
go to Database -> Rules -> Change allow read, write: if false to if true
It turns off security for the database!
It is not recommended solution for production environment but you can
use for only testing purposes
More you can find here: https://firebase.google.com/docs/firestore/security/rules-conditions

Please perform this step :
1) Open console and open your project
2) Open database -> Cloud Firestore
3) Click on RULES
4) Make allow read, write: if true instead of if false
service cloud.firestore { match /databases/{database}/documents {
match /<some_path>/ {
allow read, write: if true;
} } }
Make allow read, write: if request.auth.uid != null instead of if false
service cloud.firestore { match /databases/{database}/documents {
match /<some_path>/ {
allow read, write: if request.auth.uid != null;
} } }
This will set permission for read and write data on firestore.

Related

Failed to get downloadURL when uploading file in FirebaseStorage

I'm using FirebaseStorage in my Swift project for uploading images in a non public bucket.
Here are my rules:
In GoogleCloud Storage console:
allUser access has been removed
In FirebaseStorage console:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read: if request.auth != nil
allow write: if true;
}
}
}
With these rules, upload failed when trying to downloadURL:
let uploadTask = fileRef.putFile(from: url, metadata: metadata, completion: { (metadata, error) in
guard let _ = metadata else {
completion(nil,error)
return
}
fileRef.downloadURL { (url, error) in
completion(url,error) // <--- url is nil and error is set
return
}
})
Error is:
▿ Optional<Error>
- some : Error Domain=FIRStorageErrorDomain Code=-13021 "User does not have permission to access gs://utw6xcl26d6ywvtosast/6309669a88262d10cea863e6/35B8D02C-476E-4B6D-A51D-501CC061F047.jpg." UserInfo={ResponseErrorDomain=com.google.HTTPStatus, data={length = 73, bytes = 0x7b0a2020 22657272 6f72223a 207b0a20 ... 2e220a20 207d0a7d }, object=6309669a88262d10cea863e6/35B8D02C-476E-4B6D-A51D-501CC061F047.jpg, NSLocalizedDescription=User does not have permission to access gs://utw6xcl26d6ywvtosast/6309669a88262d10cea863e6/35B8D02C-476E-4B6D-A51D-501CC061F047.jpg., bucket=utw6xcl26d6ywvtosast, data_content_type=application/json; charset=UTF-8, ResponseErrorCode=403, ResponseBody={
"error": {
"code": 403,
"message": "Permission denied."
}
}}
If I change rules in Firebase Storage to read,write: if true this is working but resource is accessible even without access token. Which is not I want.
Do you have an idea?
Thanks!
The user who uploads the file will have to have read access to that file in order to generate a download URL for it. Given your allow read: if request.auth != nil rule, it seems like the user is not authenticated.
You might want to authenticate the user (even if just with anonymous sign-in, which doesn't require them to enter credentials) and then for example to allow them read/write access to files that are written under their own UID.

Why am I still able to fetch data, even with deleting FireStore object in Swift?

I deleted an entry in the Firestore and also checked it manually to confirm that. However, as long as I do not close the application, I can send a request to fetch the data and I still get the result. This should not be the case.
If you imagine having a shared photo with some textual information and you delete those information, this would mean, other users can still see the textual information (fetched from the Firestore) but not the image anymore (store in Firestorage).
I want to display a message on the UI, something like "The content does not exist anymore".
How I can achieve that? I used the following approach so far but it does not work at the moment:
public func checkIfChallengeObjectExists(completionHandler:#escaping(Bool)->(), challengeId:String) {
CHALLENGE_COLLECTION?.document(challengeId).getDocument(completion: { (querySnapshot, error) in
if (error != nil) {
print(error?.localizedDescription as Any)
}
if (querySnapshot?.documentID == "" || querySnapshot!.metadata.isFromCache) {
completionHandler(false)
}
else {
completionHandler(true)
}
})
}
Any solutions?
Non-existent documents will still return document snapshots, but they will be empty. Therefore, you must check the contents of the snapshot for the document, not the snapshot itself. Also, you should handle errors and the overall flow of the return better.
public func checkIfChallengeObjectExists(completionHandler:#escaping(Bool)->(), challengeId:String) {
CHALLENGE_COLLECTION?.document(challengeId).getDocument(completion: { (querySnapshot, error) in
if let doc = querySnapshot,
doc.exists {
completionHandler(true) // only one possible true condition
} else {
if let error = error {
print(error.localizedDescription)
}
completionHandler(false) // all else false
}
})
}
As a side note, I recommend reordering the parameters of the function to make it easier to read when called (conventionally, the completion handler comes last) and giving the boolean argument a name so it's easier to read when referencing (sometime later or by other developers).
public func verifyChallengeObject(ID: String, _ completion: #escaping (_ exists: Bool) -> Void) {
...
}
verifyChallengeObject(ID: "abc123", { (exists) in
if exists {
...
} else {
...
}
})

Firebase Security + Swift - Not able to read data

Having some issues with reading data. I can write just fine according to my security rules. See below for my query in swiftui code and my security rules. For context, I have a users collection and a routines collection. Each routine document has a uid that is tied to a user. Anyone know why I might not be able to read correctly here (which is affecting my ability to then subsequently write?
Security Rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
match /routines/{routine} {
allow write: if request.auth != null && request.auth.uid == request.resource.data.uid;
allow read: if request.auth != null && resource.data.uid == resource.data.uid;
}
}
}
Swift Query Code
func updateRoutine() {
db.collection("routines").whereField("name", isEqualTo: "temp routine").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
self.db.collection("users").document(self.currUser?.uid ?? "").updateData(["routinePreference": document.documentID])
self.db.collection("routines").document(document.documentID).updateData(["name": "another temp routine"])
return
}
}
}
}
EDIT
This is the error I'm getting:
Error getting documents: Error Domain=FIRFirestoreErrorDomain Code=7
"Missing or insufficient permissions."
UserInfo={NSLocalizedDescription=Missing or insufficient permissions.}
2020-07-03 01:43:24.440221-0400 TestRulesApp[58965:7804974] 6.26.0 -
[Firebase/Firestore][I-FST000001] Listen for query at routines failed:
Missing or insufficient permissions.
You are updating data here.The technique to use is incoming-field-value-equal-existing-field-value. So you should have your update rule allow update:if request.resource.data.uid == resource.data.uid;

Firestore Insufficient Permissions

I keep receiving this error:
Adding Post Error: Missing or insufficient permissions.
These are my current permissions, which lets anyone do anything (which isn't ideal, but I'm just testing).
service cloud.firestore {
match /databases/{database}/documents {
match /Posts {
allow read, write;
}
}
}
And the code I am trying to run is:
func addPost (postID: String, date: NSDate, link: String, profileID: String, text: String) {
let db = Firestore.firestore()
let settings = db.settings
settings.areTimestampsInSnapshotsEnabled = true
db.settings = settings
db.collection("Posts").document(postID).setData(["Date": date, "Link": link, "ProfileID": profileID, "Text": text]) { (error) in
if (error != nil) {
print ("Adding Post Error: " + error!.localizedDescription)
} else {
print("Post added sucessfully")
}
}
}
Why am I getting this error message? I am running the latest version of FirebaseFirestore as of June 27, 2018.
I'm pretty sure you need to specify that the user is allowed to access documents in the collection as shown in the documentation on basic read/write rules:
service cloud.firestore {
match /databases/{database}/documents {
match /Posts/{post} {
allow read, write;
}
}
}
Difference above is the {post} in match /Posts/{post}.

Unique usernames in Firebase

I have been trying to implement Chris’ answer here: Can I make Firebase use a username login process? for the Facebook login but I can’t seem to get my head around it.
So far I’ve tried to set conditions on the textField but as Firebase observer works asynchronously, the conditions to check if the username exists in the database won’t work.
let usernameString = usernameTextField.text
let uid = FIRAuth.auth()?.currentUser?.uid
ref.runTransactionBlock({ (currentData: FIRMutableData) -> FIRTransactionResult in
if var post = currentData.value as? [String : AnyObject], let uid = FIRAuth.auth()?.currentUser?.uid {
let usernamesDictionary = post["usernames"] as! NSDictionary
for (key, _) in usernamesDictionary {
if key as? String == usernameString {
print("username not available: \(key)")
}
else if usernameString == "" {
print("Uh oh! Looks like you haven't set a username yet.")
}
else if key as? String != usernameString {
print("username available: \(key)")
print("All set to go!")
let setValue: NSDictionary = [usernameString!: uid]
post["usernames"] = setValue
currentData.value = post
}
}
return FIRTransactionResult.successWithValue(currentData)
}
return FIRTransactionResult.successWithValue(currentData)
}
Then I tried creating /usernames/ node in the database and set up rules as:
{
"rules": {
"usernames": {
".read": "auth != null",
".write": "newData.val() === auth.uid && !data.exists()"
}
}
}
Now that won’t let me set any username to the database. I get confused in creating rules but my whole point is that I need a sign up flow with the username data that’s unique for each user in the database.
While trying every answer I found in related posts, what worked for me the easy way i.e. without making Firebase rules play a part in it or creating a separate usernames node in the database was to not put an if/else condition inside the Firebase observer but instead to use the exists() method of FIRDataSnapshot.
Now here’s the trick, while I did try only the exists() method with a simple observer but that did not help me. What I did was first query usernames in order, then match the username with queryEqualToValue to filter the query:
refUsers.queryOrderedByChild("username").queryEqualToValue(usernameString).observeSingleEventOfType(.Value , withBlock: {
snapshot in
if !snapshot.exists() {
if usernameString == "" {
self.signupErrorAlert("Uh oh!", message: "Looks like you haven't set a username yet.")
}
else {
// Update database with a unique username.
}
}
else {
self.signupErrorAlert("Uh oh!", message: "\(usernameString!) is not available. Try another username.")
}
}) { error in
print(error.localizedDescription)
}
}
This is the first time out of most of the answers here that worked for me. But for now, I don’t know if this would scale. Post your experiences and best practices. They’ll be appreciated.