Security - Zanegerous/CopperToGold GitHub Wiki
There is now account security:
Admin User
And limited calls per user. Users can only make 20 calls per day of each image/text and scan text. Admins are excluded from this limit and can do however many they want a day.
On account creation the user is populates with the basic data.
// Sets up basic user info on account creation
export const userSetup = async (user: any, userLevel: string) => {
const database = getDatabase();
const userUID = user.uid;
const saveRef = `users/${userUID}/Account/`
const itemRef = dbRef(database, saveRef);
try {
await set(itemRef, {
securityLevel: userLevel,
allowedQuery: 20,
allowedTextSearch: 20
});
} catch (error: any) {
console.error("Account Setup Error: ", error);
}
}
There has also been updates to change the backend to prevent non admins from creating admin users. user level can only create their own users.
The app updates daily at 00:00 or midnight refreshing the allowed user calls to the app.
export const updateAmount = onRequest(async (req, res) => {
const { state } = req.query; // Extract userID from query parameter 'state'
const userUID = state; // The userID is passed as 'state' in the request
const { target } = req.query; // Target can be 'allowedQuery' or 'allowedTextSearch'
// if its a target
if (target !== "allowedQuery" && target !== "allowedTextSearch") {
res.status(400).send("Invalid target specified.");
return; // Make sure to stop execution after sending the response
}
const availableRef = db.ref(`users/${userUID}/Account`); // Reference to the user's account in Firebase
try {
const snapshot = await availableRef.get();
// If no data exists, initialize the user with the default values, only runs if user isnt set up, only for previous accounts
if (!snapshot.exists()) {
await availableRef.set({
securityLevel: 'user', // Set default security level as 'user'
allowedQuery: 20,
allowedTextSearch: 20
});
res.status(200).send("User initialized with default values.");
return; // Ensure no further execution after sending response
}
const currentValue = snapshot.val()[target as "allowedQuery" | "allowedTextSearch"]; // Type assertion
// Ensure the query count is greater than 0 before decrementing
if (currentValue <= 0) {
res.status(400).send("Insufficient calls remaining.");
return; // Stop execution after sending response
}
// reduce count by 1
await availableRef.update({
[target]: currentValue - 1
});
res.status(200).send(`${target} updated successfully.`);
} catch (error) {
res.status(500).send("Failed to update call amount.");
}
});
Everything is now securely updated aswell from the backend
export const resetUserLimits = onSchedule("every day 00:00", async () => {
const usersRef = db.ref("users");
const snapshot = await usersRef.get();
if (!snapshot.exists()) return;
const updates: Record<string, any> = {};
snapshot.forEach(child => {
// refreshes allowed calls daily at midnight
const uid = child.key;
updates[`users/${uid}/Account/allowedQuery`] = 20; // general searches
updates[`users/${uid}/Account/allowedTextSearch`] = 20; // text scans
});
await db.ref().update(updates);
});
export const extractTextFromImage = onRequest(
{ secrets: [imageAPIKey] }, // get the secret key from firebase
async (req, res) => {
const { firebaseUrl } = req.body;
try {
const apiKey = process.env.IMAGE_TO_TEXT_APIKEY;
const response = await axios.get(
`https://api.apilayer.com/image_to_text/url?url=${encodeURIComponent(firebaseUrl)}`,
{
headers: {
apiKey,
},
}
);
const text = response.data.all_text;
res.status(200).send({ text });
} catch (error: any) {
console.error('Failed:', error);
res.status(500).send('Failed to process image');
}
}
);