Firestore Insufficient Permissions - swift

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}.

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.

SwiftUI: Check if Firebase RealtimeDatabase has a specific Child the register the value or return error

I am currently building an app with an account system.
Firebase is very new to me, that's why I watched a lot of tutorials, and now its working fine.
I want to implement that the user can choose a unique username at the registration. My problem is, I really don't know how to check if this name is already taken.
I found some code for that, but that's not working, I will show you the code for the RegistrationService file.
I hope someone can explain to me how to implement this username verification. It should return an error if the username is already taken and do continue the registration if its a valid username.
Thank you!
import Combine
import Firebase
import FirebaseDatabase
import Foundation
enum RegistrationKeys: String {
case firstName
case lastname
case info
case username
}
protocol RegisterService {
func register(with details: RegistrationDetails) -> AnyPublisher<Void, Error>
}
final class RegisterServiceImpl: RegisterService {
func register(with details: RegistrationDetails) -> AnyPublisher<Void, Error> {
Deferred {
Future { promise in
Auth.auth()
.createUser(
withEmail: details.email,
password: details.password
) { res, error in
if let err = error {
promise(.failure(err))
} else {
// Success on User creation
if let uid = res?.user.uid {
let values =
[
RegistrationKeys.firstName.rawValue: details.firstName,
RegistrationKeys.lastname.rawValue: details.lastName,
RegistrationKeys.info.rawValue: details.info,
] as [String: Any]
let db = Database.database(url: "theurl")
Database.database(url: "the url")
.reference()
.child("usernames")
.child("\([RegistrationKeys.info.rawValue: details.username] as [String : Any])")
// here should be the check and then continue if its valid
db
.reference()
.child("users")
.child(uid)
.updateChildValues(values) { error, ref in
if let err = error {
promise(.failure(err))
} else {
promise(.success(()))
}
}
} else {
promise(.failure(NSError(domain: "Invalid user ID", code: 0, userInfo: nil)))
}
}
}
}
}
.receive(on: RunLoop.main)
.eraseToAnyPublisher()
}
}
I can see two possibilities to solve your problem:
If the e-mail can serve as the username
Firebase authentication already sends back an error message in case the e-mail (the one used when creating the user) already exists. If the e-mail passed in the following function is not unique, an error will be thrown:
Auth.auth()
.createUser(
withEmail: details.email,
password: details.password
) { res, error in
if let err = error {
promise(.failure(err))
If an additional username besides the e-mail is required
If you need usernames in addition to the e-mails, you can store them under a node "usernames", like we see in your example. Personally, I would hash them instead of storing them plain.
The structure could simply be:
{
usernames: {
username_1: true,
username_2: true,
...
username_n: true
}
}
The example below checks to see if a new username exists and stores the result in the variable isUsernameTaken:
let db = Database.database(url: "the url").reference()
let newUsername = "seeIfItIsTaken"
db.child("usernames").child(newUsername).getData() { error, snapshot in
guard error == nil else {
print("Found error \(error)")
return
}
let isUsernameTaken = snapshot.exists()
}

amplify subscription using auth in swift

I want to set up a list of live list of "Move"s so I used this snippet from the amplify docs.
func createSubscription() {
subscription = Amplify.API.subscribe(request: .subscription(of: Move.self, type: .onCreate))
dataSink = subscription?.subscriptionDataPublisher.sink {
if case let .failure(apiError) = $0 {
print("Subscription has terminated with \(apiError)")
} else {
print("Subscription has been closed successfully")
}
}
receiveValue: { result in
switch result {
case .success(let createdTodo):
print("Successfully got todo from subscription: \(createdTodo)")
case .failure(let error):
print("Got failed result with \(error.errorDescription)")
}
}
}
Schema auth rules
type Move
#model
#auth( rules: [
{ allow: owner, ownerField: "owner", operations: [create, update, delete, read] },
])
{
But since I added auth to the "move" type I get this error. GraphQLResponseError<Move>: GraphQL service returned a successful response containing errors: [Amplify.GraphQLError(message: "Validation error of type MissingFieldArgument: Missing field argument owner # \'onCreateMove\'", locations: nil, path: nil, extensions: nil)]
and Recovery suggestion: The list of GraphQLError contains service-specific messages
So everything is working locally but I think I need to pass the authorization to the request but I can't find any way to do it. any Ideas how I might get this request to process properly?
got it working by writng my own request and passing the owner field directly
extension GraphQLRequest {
static func newMoves() -> GraphQLRequest<Move> {
let operationName = "getMove"
let document = """
subscription MySubscription {
onCreateMove(owner: "MyUser") {
accerationMagnitude
id
}
}
"""
return GraphQLRequest<Move>(document: document,
// variables: [],
responseType: Move.self,
decodePath: operationName)
}
}

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;

Swift: Firestore adding new data gives error

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.