I am trying to set the location of a user by using GeoFire's setLocation but I am getting a permission denied error.
String uid = (Provider.of<UserData>(context, listen: false).uid);
print(uid);
bool? response = await Geofire.setLocation(uid, location.latitude ?? 0.0, location.longitude ?? 0.0);
print(response);
Here is the output:
I/flutter (23479): z9jbb4W9gvbfkOt9mnPhsJBNxSX2
I/TAG (23479): setLocation
W/RepoOperation(23479): setValue at /users/z9jbb4W9gvbfkOt9mnPhsJBNxSX2 failed: DatabaseError: Permission denied
I/flutter (23479): false
Here are my gradle files:
android/build.gradle
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:multidex:1.0.3'
implementation 'com.google.firebase:firebase-core:16.0.0'
implementation 'com.google.firebase:firebase-auth:16.0.1'
implementation 'com.google.firebase:firebase-database:12.0.1'
implementation 'com.google.firebase:firebase-storage:12.0.1'
implementation 'com.google.firebase:firebase-appcheck:16.0.0'
implementation 'com.firebase:geofire-android:2.3.1'
}
app/build.gradle
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.0.1'
}
and here are my database rules on firestore:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
match /users/{userId}/{documents=**} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
}
}
I have tried setting the rules to True for everything and still get the same error.
Related
Hello Guys I had created the flutter application in which when a user pressed signup button then they will be checked that provided email exists in the firestore database or not if no then they will be move to next screen. I am getting this [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: [cloud_firestore/permission-denied] The caller does not have permission to execute the specified operation. exception even everything looks fine in my code and firestore configuration
Here is my firestore database rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}
Here is my project level build.gradle:
buildscript {
ext.kotlin_version = '1.6.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.13'
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Here is my app-level build.gradle
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.recipedia.fyp.recipedia"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation platform('com.google.firebase:firebase-bom:31.0.3')
}
Here is my signup code:
onPressed: () async {
if (nameTextController.text.isEmpty) {
displayToastMessage(
"Please enter name", context);
} else if (nameTextController.text.length < 3) {
displayToastMessage("Name must be atleast 3 characters", context);
} else if (nameTextController.text.contains(RegExp(r'[0-9]'))) {
displayToastMessage("Numbers and special characters cannot be included", context);
} else if (emailTextController.text.isEmpty) {
displayToastMessage("Please enter email", context);
} else if (!emailTextController.text.contains('#')) {
displayToastMessage("Please enter a valid email", context);
} else if (passwordTextController.text.isEmpty) {
displayToastMessage("Please enter password", context);
} else if (passwordTextController.text.length < 6) {
displayToastMessage("Password must be at-least 6 Characters", context);
} else {
print('Before emailExists');
emailExists = await UserModel().checkIfEmailExists(email);
print('Email exist: $emailExists');
if (emailExists == true) {
snackBar(context, 'Email is already registered');
} else {
/*Move to next screen*/
}
}
}
User Model class checkIfEmailExist function
Future<bool> checkIfEmailExists(String email) async {
try {
var collectionReference = FirebaseFirestore.instance.collection('users');
var doc = await collectionReference.doc(email).get();
return doc.exists;
} catch (e) {
rethrow;
}
}
Solution: In Firestore database rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
The database structure is like so:
suppliers(collection)>user_email(document)
customers(collection)>user_email(document)>foo(field)
And the current firestore.rules is like so:
match /databases/{database}/documents {
//Base rule - fully restrictive
match /{document=**} {
allow read, write: if false;
}
//Checks if user is registered in the suppliers section
function isSupplierTeam(request) {
return exists(/suppliers/$(request.auth.token.email));
}
// Supplier self-data access
match /suppliers/{email} {
allow read, write: if request.auth.token.email == email;
}
match /suppliers/{email}/{document=**} {
allow read, write: if request.auth.token.email == email;
}
// Checking the Customer Sub-Section
match /customers/{email} {
allow read, write: if request.auth.token.email == email || isSupplierTeam(request);
}
match /customers/{email}/{document=**} {
allow read, write: if request.auth.token.email == email || isSupplierTeam(request);
}
}
And the query run in javascript, when logged in as a "supplier" is:
db.collection("customers").get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(doc.id);
console.log(doc.data());
});
})
.catch((error) => {
console.log("Error getting documents: ", error);
});
It doesn't seem to work and instead triggers a missing/insufficient permission error. How should I tweak my firestore.rules to allow suppliers to access the customer data?
So this works:
//Checks if user is registered in the suppliers section
function isSupplierTeam(request) {
return exists(/databases/$(database)/documents/suppliers/$(request.auth.token.email));
}
It seems like the full "URL" is needed to access the relevant firestore document.
I have a NSFW system where it checks the document if it is NSFW and if it is it updates the document field isNSFW to true. That works just fine but now I wanted to not show those documents to all users via settings a rule instead of querying it out.
This is what I have but it's not working...
javascript
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth.uid != null;
}
}
match /users/{user} {
allow read: if true;
}
match /docs/{doc} { // THIS HERE
allow read: if resource.data.isNSFW == false;
}
}
I tried adding request. before the resource and it still didn't work.
UPDATE:
javascript
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// match /{document=**} {
// allow read, write: if request.auth.uid != null;
// }
match /users/{user} {
allow read: if currentUser().uid != null;
}
match /docs {
allow read: if existingData().users[currentUser().uid] == false;
}
match /docs/{doc} {
allow write: if currentUser().uid != null;
}
}
// MARK - Funcs ---------------
function existingData() {
return resource.data
}
function incomingData() {
return request.resource.data
}
function currentUser() {
return request.auth
}
function isSignedIn() {
return request.auth != null;
}
}
Getting error:
Listen for Query(docs where users.`40S88coPQObEWSeiYMZIJlIKJkI2` == false order by __name__) failed: Status{code=PERMISSION_DENIED, description=Missing or insufficient permissions., cause=null}
If you are using Firebase Datastore, you can simply query 1
your data:
var myDB = db.collection("YOUR-DB-COLLECTION");
var query = myDB.where("isNSFW", "==", false);
If you are using GCP datastore, you do it this other way 2:
const query = datastore
.createQuery('YOUR-COLLECTION')
.filter('isNSFW', '=', false);
Your matches are not nested correctly. They should be inside the block that starts with match /databases/{database}/documents. Also, you should strongly consider removing the match on /{document=**} because that will let everyone read any document in the database if they're logged in, ignoring all other rules.
I am new to using firebase and ios development in general. I am having an issue with adding user's info to the firestore database even though they are being added as authenticated users. Any Suggestions?
Auth.auth().createUser(withEmail: email, password: password) { (result, err) in
if err != nil {
self.errorLabel.text = "Error Creating User"
self.errorLabel.alpha = 1
} else {
let db = Firestore.firestore()
db.collection("users").addDocument(data: ["firstname":firstname, "lastname":lastname, "uid":result!.user.uid]) { (error) in
if error != nil {
self.errorLabel.text = "error saving user data"
self.errorLabel.alpha = 1
}
}
self.transitionScreens()
}
}
}
}
Change your code to the following:
// Add a new document with a generated ID
var ref: DocumentReference? = nil
ref = db.collection("users").addDocument(data: [
"firstname": firstname,
"lastname": lastname,
"uid": result!.user.uid
]) { err in
if let err = err {
print("Error adding document: \(err)")
} else {
print("Document added with ID: \(ref!.documentID)")
}
}
Using this print statement print("Error adding document: \(err)") you can know exactly what the error is.
Also change your security rules to the following:
// Allow read/write access to all users under any conditions
// Warning: **NEVER** use this rule set in production; it allows
// anyone to overwrite your entire database.
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
Check out the following different rules you can give access to users depending on the data
service cloud.firestore {
match /databases/{database}/documents {
//allows all users to read and write, but dangerous as any one can flood your database
match /public_collection/{document=**} {
allow read, write: if true;
}
//only read access
match /public_read_collection/{document=**} {
allow read: if true;
allow write: if false;
}
//prefered for storing users personal info, users can access only their data
match /users/{userId} {
allow read, write: if request.auth.uid == userId;
}
//any authenticated user can access or write data
match /posts/{documentId} {
allow read, write: if request.auth.uid != null;
}
}
}
I'm using Firebase Geofire to get user locations to store them in my database. I get an error when I try to run a query:
Listener at /RDio5CZF2fcJTUOcaeNJhuvYP2Q2 failed: permission_denied
2017-09-19 22:34:24.809 HeartQuest[26419] <Warning> [Firebase/Database][I-RDB038012] Listener at /RDio5CZF2fcJTUOcaeNJhuvYP2Q2 failed: permission_denied
An error occurred getting the location for "firebase-hq": Optional("Permission Denied")
The errors occur after running the following query:
let userID = User.current?.key
let geofireRef = Database.database().reference()
let geoFire = GeoFire(firebaseRef: geofireRef)
geoFire?.getLocationForKey(userID!, withCallback: { (location, error) in
if (error != nil) {
print("An error occurred getting the location for \"firebase-hq\": \(error?.localizedDescription)")
} else if (location != nil) {
print("Location for \"firebase-hq\" is [\(location?.coordinate.latitude), \(location?.coordinate.longitude)]")
} else {
print("GeoFire does not contain a location for \"firebase-hq\"")
}
})
}
Can anyone please explain what the location key is and why I might not have permission for it? I'm using CLLocationManager to request the user's permission for their phone's location.
EDIT: Firebase rules
{
"rules": {
".validate": "auth != null",
".write": "auth != null",
"users" : {
".read": "auth != null",
".indexOn" : ["email","username"],
"$userId": {
".write": "data.val() == null || (newData.val() == null && $userId === auth.uid)"
}
}
}
}