As per documentation written in FlutterFire about Firestore bundles, I was unable to create such a thing. It seems documentation lacks something as all I get was error codes: Permission is denied
static const bucket = "felix-3610.appspot.com";
static const storage = "https://storage.cloud.google.com";
final Message msg = Get.find();
Future<QuerySnapshot<Map<String, Object?>>> getEpic() async {
QuerySnapshot<Map<String, Object?>>? snapshot;
const path = "$storage/$bucket/music/bundles/epic_bundles.txt";
Reference httpsReference = FirebaseStorage.instance.refFromURL(path);
try {
String url = await httpsReference.getDownloadURL();
final response = await http.get(Uri.https(url));
Uint8List buffer = Uint8List.fromList(response.body.codeUnits);
LoadBundleTask task = FirebaseFirestore.instance.loadBundle(buffer);
await task.stream.last.then((value) async {
snapshot = await FirebaseFirestore.instance.collection("latest-epic").get(const GetOptions(source: Source.cache));
});
} on FirebaseException catch (e) {
log(e.code); //unauthorized
}
return snapshot!;
}
I could not know where I went wrong. Have people tried to code and load Firestore bundles in Flutter? Please, I do need a good documentation of how to do it correctly.
E/StorageException(24282): StorageException has occurred.
E/StorageException(24282): User does not have permission to access
this object. E/StorageException(24282): Code: -13021 HttpResult: 403
E/StorageException(24282): { "error": { "code": 403, "message":
"Permission denied." }} E/StorageException(24282):
java.io.IOException: { "error": { "code": 403, "message":
"Permission denied." }} E/StorageException(24282): at
com.google.firebase.storage.network.NetworkRequest.parseResponse(NetworkRequest.java:445)
E/StorageException(24282): at
com.google.firebase.storage.network.NetworkRequest.parseErrorResponse(NetworkRequest.java:462)
E/StorageException(24282): at
com.google.firebase.storage.network.NetworkRequest.processResponseStream(NetworkRequest.java:453)
E/StorageException(24282): at
com.google.firebase.storage.network.NetworkRequest.performRequest(NetworkRequest.java:272)
E/StorageException(24282): at
com.google.firebase.storage.network.NetworkRequest.performRequest(NetworkRequest.java:289)
E/StorageException(24282): at
com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(ExponentialBackoffSender.java:76)
E/StorageException(24282): at
com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(ExponentialBackoffSender.java:68)
E/StorageException(24282): at
com.google.firebase.storage.GetDownloadUrlTask.run(GetDownloadUrlTask.java:77)
E/StorageException(24282): at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
E/StorageException(24282): at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
E/StorageException(24282): at java.lang.Thread.run(Thread.java:764)
The way I generated bundle is using Cloud Scheduler, like in this example below:
// #ts-check
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const firestore = admin.firestore();
const fs = require("fs");
// This will create it every month
exports.BundleRelax = functions.pubsub.schedule("0 0 1 * *").retryConfig({
retryCount: 3,
maxRetryDuration: "30s",
}).onRun(async (context) => {
const bundleId = "latest-relax";
const bundle = firestore.bundle(bundleId);
const querySnapshot = await firestore.collection("Music_Relax").where("status.state", "==", 1).get();
// Build the bundle
const bundleBuffer = bundle.add("latest-relax-query", querySnapshot).build();
// Create tmp local file to upload
const bundledFilePath = "/tmp/bundle.txt";
fs.writeFileSync(bundledFilePath, bundleBuffer);
// Upload bundle file to Storage
const bucket = "felix-366510.appspot.com";
const destination = "relax_bundle.txt";
await admin.storage().bucket(bucket).upload(bundledFilePath, {
destination,
public: true,
metadata: {
cacheControl: "public, max-age=3110400",
},
}).then((value) => {
return Promise.resolve("success");
});
});
Security Rules for Cloud Storage:
rules_version = '2';
//Read operation: get, list
//Write operation: create, update, delete
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if isAdmin();
}
match /music {
allow read: if isIDN();
allow get, list, create, update: if isPublisher();
}
match /server {
allow read, create, update: if isUser();
}
match /publishers {
allow read: if isUser();
allow get, list, create, update: if isPublisher();
}
match /clients {
allow read, write: if isFelix();
}
//This is for any subcription inside Felix Apps
//Value 0 is equal to FALSE
//Value 1 is equal to TRUE
//This is for all versions
function isMT() {
return mtIDN() || mtEN() || mtCHN();
}
function isMTtrial() {
return trMT_IDN() || trMT_EN() || trMT_CHN();
}
//IDN version code 1
//Trials here
function trMT_IDN(){
return isIDN() && (request.auth.token.T11 == 1);
}
//Subs here
function mtIDN(){
return isIDN() && (request.auth.token.MT1 == 1);
}
//EN version code 2
//Trials here
function trMT_EN(){
return isEN() && (request.auth.token.T21 == 1);
}
//Subs here
function mtEN(){
return isEN() && (request.auth.token.MT2 == 1);
}
//CHN version code 3
//Trials here
function trMT_CHN(){
return isCHN() && (request.auth.token.T31 == 1);
}
//Subs here
function mtCHN(){
return isCHN() && (request.auth.token.MT3 == 1);
}
function isFelix(){
return isIDN() || isEN() || isCHN();
}
function isIDN(){
return isUser() && request.auth.token.IDN == true;
}
function isEN(){
return isUser() && request.auth.token.EN == true;
}
function isCHN(){
return isUser() && request.auth.token.CHN == true;
}
function isUser() {
return (isClient() || isServer());
}
function isPublisher(){
return (isMusician() || isGross() || isNet() || isFree());
}
function isMusician(){
return isUser() && roleNew();
}
function isNet(){
return isUser() && roleNet();
}
function isGross(){
return isUser() && roleGross();
}
function isFree(){
return isUser() && roleFree();
}
function isAdmin(){
return request.auth.uid != null && request.auth.token.Admin == true;
}
function isServer(){
return request.auth.uid != null && request.auth.token.HaD == true;
}
function isClient(){
return request.auth.uid != null && request.auth.token.Felix == true;
}
//This is for roles claim inside HaD App
function roleNew(){
return request.auth.token.role == "Unregistered";
}
function roleNet(){
return request.auth.token.role == "Net";
}
function roleGross(){
return request.auth.token.role == "Gross";
}
function roleFree(){
return request.auth.token.role == "Free";
}
}
}
Related
For some reason when I try to use the resource.data I get an error saying that resource is undefined. In this case it occurs in the notUpdating() function. Why is this and how can I fix it?
I'm new to firebase - Thanks for the help!
Error: simulator.rules line [12], column [28]. Property resource is undefined on object.
Rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
function authed() {
return request.auth != null
}
function matchesUser() {
return request.auth.uid == userId
}
function notUpdating(field) {
return !(field in request.resource.data) || resource.data[field] == request.resource.data[field]
}
match /folders/{docId} {
allow read: if authed() && matchesUser()
allow create: if authed() && matchesUser()
}
match /files/{docId} {
allow read: if authed() && matchesUser()
allow create: if authed() && matchesUser()
allow update: if authed() && matchesUser() && notUpdating("userId")
}
}
}
}
My code for adding and updating a file:
function addFile() {
if (!filePathInp.value) return;
const fileName = filePathInp.value.split("\\").pop();
const fileRef = ref(storage, `users/${$user.uid}/files/${fileName}`);
const uploadTask = uploadBytesResumable(fileRef, filePathInp.value);
uploadTask.on(
"state_changed",
(snapshot) => {},
(error) => {
console.log(error);
},
() => {
// upload complete
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
const files = collection(firestore, `users/${$user.uid}/files`);
const filesQuery = query(
files,
where("name", "==", fileName),
where("userId", "==", $user.uid),
where("parentPath", "==", path)
);
getDocs(filesQuery).then((doc) => {
if (doc.empty) {
addDoc(files, {
url: downloadURL,
name: fileName,
createdAt: new Date(),
parentPath: path,
userId: $user.uid,
});
} else {
updateDoc(doc.docs[0].ref, {
url: downloadURL,
});
}
});
});
}
);
filePathInp.value = "";
}
These are my rules. User can only access accounts associated with that user (account document has field users containing uid)
function isLoggedIn() {
return request.auth != null;
}
function getAccount(accountID) {
return get(/databases/$(database)/documents/accounts/$(accountID)).data;
}
function isBelongTo(accountID) {
return isLoggedIn() && (request.auth.uid in getAccount(accountID).users);
}
match /accounts/{accountID}/{documents=**} {
allow read: if isBelongTo(accountID);
}
This works fine on the rules playground. But when I do a query in firebase javascript sdk in a browser like this
onSnapshot(query(collection(db, "accounts"), where('users', 'array-contains', uid ? uid : '')), (querySnapshot) => {
const docs = [];
// querySnapshot does not have .map, so we have to use .forEach
querySnapshot.forEach((doc) =>
docs.push({ ...doc.data(), id: doc.id })
);
console.log(docs);
});
it returns an empty array (in my larger project but not the demo linked below) and returns a permission error.
if i first get a single document with the below code, then the query above returns the document /accounts/35 in the query snapshot, and also returns a permission error:
onSnapshot(doc(db, "accounts", "35"), (doc) => {
if (doc.exists) {
// console.log("Document data:", doc.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
})
I'm made a sample repo and here is the result:
How can I resolve this?
I get the error code "permission-denied" with error message "Missing or insuffiecient permisions" and I'm not sure why, I'm new to this.
Here is the rule defined in firestore:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function depositRecordAuth() {
return request.auth != null &&
resource.data.cid == request.auth.uid &&
timestamp.value(request.auth.token.expires)>= request.time;
}
match /deposit/{docId} {
allow read , delete: if
depositRecordAuth();
}
}
}
And the only piece of code which throws the error is: resource.data.cid == request.auth.uid
If I remove it, it will work.
An this is the code which throws the error:
fireStoreDocListener = (collection: string, docId: string, CallBack: (data: firebase.firestore.DocumentData | undefined) => void) =>
this.firestore?.collection(collection).doc(docId)
.onSnapshot((doc) => {
const docData = doc.data();
CallBack(docData);
}, (err: IFireBaseError) => {
let errorDescription = "";
if (err.code === 'permission-denied') {
//do somthing
errorDescription = `firebase error on doc listener , error code - ${err.code} , error message - ${err.message}`;
} else {
//do somthing else
errorDescription = `firebase error on doc listener , error code - ${err.code} , error message - ${err.message}'`;
}
})
Updated with logging:
the console.log(collection, docId) has
Collection: deposit
docId: 109003
and the doc in firebase:
doc
I'm just trying to have the rules be that if the user is authenticated then they can write. When I try to upload an image to the gallery from the website I get back an error saying that I'm not authorized. I have authentication set up and working. Here are my rules:
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
And here is my image upload code:
imgUploadHandler = () => {
const image = this.props.imageUpload;
const uploadTask = storage.ref(`/Gallery/${image.name}`).put(image);
uploadTask.on('state_changed',
(snapshot) => {
// Progress function
const progress = Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
this.setState({ progress });
},
(error) => {
// Error function
console.log(error);
},
() => {
// Complete function
storage.ref('Gallery').child(image.name).getDownloadURL()
.then(URL => {
const imageData = { imgURL: URL, name: image.name, width: "400"}
this.props.onImageUploaded(URL);
this.imgSelectedHandler(URL);
Axios.post(`/Gallery.json?auth=${this.props.token}`, imageData);
})
})
}
If I create a new user with createUserWithEmailAndPassword, even though I didn't verify the mail yet, that user is already logged in. And his .emailVerified === false, and until here all good.
Now, I go to the mail, verify it using the link, go back to the web app, it is still .emailVerified === false so I refresh the page, now .emailVerified === true.
So I try to reach this doc:
public async getPublicUserDetails() {
const currentUserId = this._angularFireAuth.auth.currentUser.uid;
try {
const docRef = this._angularFirestore.collection("users").doc(currentUserId).ref;
const doc = await docRef.get();
if (!doc.exists) {
return null;
}
return doc.data() as IPublicUserDetailsDto;
}
catch (error) {
console.error("User " + currentUserId + " details get failed! " + JSON.stringify(error));
throw error;
}
}
It catches an exception, saying I don't have the required permissions to access the doc.
The Firestore rules I'm using are:
rules_version = '2';
service cloud.firestore {
function dbDocs() { return /databases/$(database)/documents; }
function isSignedIn() { return request.auth != null && request.auth.uid != null; }
function isEmailVerified() { return isSignedIn() && request.auth.token.email_verified; }
function isCurrUser(uid) { return isSignedIn() && request.auth.uid == uid; }
function userExists(uid) { return exists(/databases/$(database)/documents/users/$(uid)); }
match /databases/{database}/documents {
match /users {
match /{userId} {
allow read: if isEmailVerified();
allow write: if isEmailVerified() && isCurrUser(userId);
}
}
}
}
I can refresh the page infinite times, but it will work only if I signOut & signIn again OR if I replace the allow read line with
match /{userId} {
allow read: if isSignedIn(); // replace this
allow write: if isEmailVerified() && isCurrUser(userId);
}
Conclusion: it seems like the request.auth.token.email_verified does not reflect the value provided inside the FirebaseAuth service, as it seems to get refreshed only if I log out and back in.
Can someone help me, please? Thank you all in advance!