I'm currently using Stream to build Feeds in our React Native app.
I ran into a problem enabling users to post to a new board feed group I made.
Currently iOS only
Problem: I have created a "board" feed group and want to allow users to post to that feed group to save posts. I have enabled permissions for this as per: https://github.com/GetStream/react-activity-feed/issues/23 via support. Yet I am still unable to post to the "board" feed group but I'm still receiving a permissions error.
I'm using the SinglePost component from the official React Native GetStream library and have a save button on the activity component.
The save button opens a modal to allow a user to select a board and after a user selects a board the _saveBoard method is called. You can see the full method here.
_saveToBoard = item => {
let boardId = item.id;
console.log('boardId', boardId, this.props);
// // Add activity to selectedBoard
var selectedBoard = this.props.client.feed('board', boardId);
let activity_data = {
actor: this.props.client.currentUser.id,
verb: 'save',
object: `BoardPost:${generateId()}`
};
console.log('activity data', activity_data);
selectedBoard
.addActivity(activity_data)
.then(res => console.log('added activity', res))
.catch(err => console.log('error adding activity to board', err));
console.log('selectedBoard', selectedBoard);
};
Not sure where I went wrong and hoping its something I overlooked.
The default permission policies don't allow a user to add an activity with actor set to anything else than current user's reference. This restriction is only enforced client-side to make sure that users are not able to impersonate other users.
Changing actor from this.props.client.currentUser.id into this.props.client.currentUser.id will make sure that the activity is added with a user reference equal to current user.
Related
I have a bot developed using the Bot Framework v4 using NodeJS and deployed on multiple channels in Teams. Is there a way we can update a message sent by the bot? I tried implementing the updateActivity() function in the BotFrameworkAdapter. However, it does not update the activity.
I have this card sent from the bot to a Teams channel. When someone clicks on the button, is there a way I can update the card or the message (disabling the button)?
The key to this is making sure that when you use updateActivity(), you use the right activity ID that is created by the Teams Channel. You also need to make sure that the updated activity gets all of the Teams data set to it.
In onTurn, capture outgoing activities so that you can easily save all of the necessary Teams Channel data:
public onTurn = async (turnContext: TurnContext) => {
turnContext.onSendActivities(async (ctx, activities, nextSend) => {
activities.forEach(async (activity) => {
if (activity.channelData.saveMe) {
this.savedActivity = activity;
}
});
return await nextSend();
});
Note: There might be another way to do this. I just found this to be the easiest, since you need to save all of the channelData, conversation info, and activity.id, at a minimum
How you store that activity to be used later is up to you. If you store it in the constructor, it will either be re-instantiated on every message (C# SDK) or any user has the ability to change it (JS SDK). You might consider writing custom storage.
Activities keep all channelData. By specifying a saveMe flag, we ensure we save the right activity
Instantiate some key variables:
const teamsChannel = '19:8d60061c3d10xxxxxxxxxxxxxxxx#thread.skype';
const serviceUrl = 'https://smba.trafficmanager.net/amer/';
Note: the easiest way to get these variables is to send a message from Teams to the bot while putting a breakpoint on the incoming activity
serviceUrl likely varies by geo region
Send the first activity and store the ID:
// This ensures that your bot can send to Teams
turnContext.activity.conversation.id = teamsChannel;
turnContext.activity.serviceUrl = serviceUrl;
MicrosoftAppCredentials.trustServiceUrl(serviceUrl);
// Add the saveMe flag
yourActivity.channelData = { saveMe: true };
const response = await turnContext.sendActivity(yourActivity);
this.activityToUpdateId = response.id;
How you store that ID to be used later is up to you. If you store it in the constructor, it will either be re-instantiated on every message (C# SDK) or any user has the ability to change it (JS SDK). You might consider writing custom storage.
Update your saved activity:
// New data
const card2 = CardFactory.adaptiveCard(adaptiveCard2);
// Set the saved activity.id and new activity data (an adaptiveCard, in this example)
this.savedActivity.id = this.activityToUpdateId;
this.savedActivity.attachments = [card2];
Send the update:
await turnContext.updateActivity(this.savedActivity);
Note: you can update the activity with anything. I swapped out entirely different Adaptive Cards
Before:
After:
I've tried this using the middleware but keep getting: "The bot is not part of the conversation roster". Question: My bot is updating a message that a user wrote, so do I need special permissions?
let ActivityID = context.activity.conversation.id.split("=")[1];
let updatedActivity: Partial<Activity> = {
"id": ActivityID,
"channelId": context.activity.channelId,
"channelData": context.activity.channelData,
"conversation":
{
"name": "",
"id": context.activity.conversation.id,
"isGroup": context.activity.conversation.isGroup,
"conversationType": context.activity.conversation.conversationType,
"tenantId": context.activity.conversation.tenantId
},
"type": "message",
"text": "",
"summary": "",
"attachments": [ attachment ]
} await context.updateActivity(updatedActivity);
I'm struggling with firebase custom claims.
I have tested a lot of approaches nothing works. Obviously, I miss something important in the concept itself.
So I'm back to the root. This script from the google example should apply customs rule on a newly created user
exports.processSignUp = functions.auth.user().onCreate(event => {
const user = event.data; // The Firebase user.
const customClaims = {
param: true,
accessLevel: 9
};
// Set custom user claims on this newly created user.
return admin.auth().setCustomUserClaims(user.uid, customClaims)
});
Then on a client, I check the result with
firebase.auth().currentUser.getIdTokenResult()
.then((idTokenResult) => {
// Confirm the user is an Admin.
console.log(idTokenResult.claims)
if (!!idTokenResult.claims.param) {
// Show admin UI.
console.log("param")
} else {
// Show regular user UI.
console.log("no param")
}
})
.catch((error) => {
console.log(error);
});
Everything just a raw copy-paste still doesn't work. I've tested both from the local machine(there could be troubles with cors?) and deployed
This is a race situation. If the Function end first then, you will get the updated data.
The getIdTokenResult method does force refresh but if the custom claim is not ready then, it is pointless.
You need to set another data control structure to trigger the force refresh on the client. By example a real-time listener to the rtd;
root.child(`permissions/${uid}`).on..
And the logic inside the listener would be: if the value for that node exists and is a number greater than some threshold, then trigger the user auth refresh
During that time the ui can reflect a loading state if there is no datasnapshot or the not admin view if the datasnapshot exists but is a lower permission level.
In Functions you have to set the node after the claim is set:
..setCustomUserClaims(..).then(
ref.setValue(9)
);
I have a more detailed example on pastebin
The claims on the client are populated when the client gets an ID token from the server. The ID token is valid for an hour, after which the SDK automatically refreshes it.
By the time the Cloud Functions auth.user().onCreate gets called, the client has already gotten the ID token for the new user. This means that it can take up to an hour before the client sees the updated claims.
If you want the client to get the custom claims before that, you can force it to refresh the token. But in this video our security experts recommend (that you consider) using a different storage mechanism for claims that you want to be applied straight away.
The database structure looks like this
-LGw89Lx5CA9mOe1fSRQ {
uid: "FzobH6xDhHhtjbfqxlHR5nTobL62"
image: "https://pbs.twimg.com/profile_images/8950378298..."
location: "Lorem ipsum, lorem ipsum"
name: "Lorem ipsum"
provider: "twitter.com"
}
How can I delete everything, including the -LGw89Lx5CA9mOe1fSRQ key programmatically?
I looked at this, but it's outdated and deprecated Firebase: removeUser() but need to remove data stored under that uid
I've also looked at this, but this requires for user to constantly sign in (I'm saving the user ID in localStorage) and it returns null on refresh if I write firebase.auth().currentUser. Data records and user accounts are created through social network providers and I can see the data both on Authentication and Database tab in the Firebase console.
I've tried with these piece of code but it does nothing.
// currentUser has a value of UID from Firebase
// The value is stored in localStorage
databaseChild.child(currentUser).remove()
.then(res => {
// res returns 'undefined'
console.log('Deleted', res);
})
.catch(err => console.error(err));
The bottom line is, I need to delete the user (with a specific UID) from the Authentication tab and from the Database at the same time with one click.
I know that there is a Firebase Admin SDK but I'm creating a Single Page Application and I don't have any back end code. Everything is being done on the front end.
Any kind of help is appreciated.
With suggestions from #jeremyw and #peter-haddad I was able to get exactly what I want. Here is the code that is hosted on Firebase Cloud Functions
const functions = require('firebase-functions'),
admin = require('firebase-admin');
admin.initializeApp();
exports.deleteUser = functions.https.onRequest((request, response) => {
const data = JSON.parse(request.body),
user = data.uid;
// Delete user record from Authentication
admin.auth().deleteUser(user)
.then(() => {
console.log('User Authentication record deleted');
return;
})
.catch(() => console.error('Error while trying to delete the user', err));
// Delete user record from Real Time Database
admin.database().ref().child('people').orderByChild('uid').equalTo(user).once('value', snap => {
let userData = snap.val();
for (let key of Object.keys(userData)) {
admin.database().ref().child('people').child(key).remove();
}
});
response.send(200);
});
Also, if you are facing CORS errors, add the mode: 'no-cors' option to your fetch() function and it will work without any problems.
The link you already found for deleting the user-login-account client-side is your only option if you want to keep the action on the client. Usually you want to keep most of the actions for things like account creation/deletion on the server for security reasons, and Firebase forces the issue. You can only delete your account if you were recently logged in, you can't have client-side start deleting old/random accounts.
The better option is to create your own Cloud Function to handle everything related to deleting a user. You would have to use the Admin SDK that you already found for this... but you could have that Cloud Function perform as many actions as you want - it will have to delete the user from the Auth tab, and delete the matching data in the Database.
Hello I am using the package node-craigslist https://github.com/brozeph/node-craigslist, and am hoping that someone can help me with some syntax with the details method.
In his documentation, he gives the example
client
.list()
.then((listings) => client.details(listings[0]))
.then((details) => {
console.log(details);
})
.catch((err) => {
console.error(err);
});
I currently have working the code to get the listings, but not the details. Below is my section of code where I retrieve the listings.
client
.list(options)
.then((listings) => {
listings.forEach((listing) => {
console.log(listing);
searchResults.title[counter] = listing.title;
searchResults.date[counter] = listing.date;
searchResults.location[counter] = listing.location;
searchResults.url[counter] = listing.url;
searchResults.price[counter] = listing.price;
counter++;
if(counter === listings.length) {
socket.emit('redirect'); //change to /results.ejs if done looping
};
});//end of listings foreach
});
I have been trying to incorporate the details into my own code unsuccessfully. Does anybody have any knowledge on how I can do this? Thanks.
You need to pass in a listing object into the method. In his example, he just grabs the most recent listings without any search options, then passes in the first listing from that array of listings. But you could obviously customize the search and the options.
client
.list()
.then((listings) => client.details(listings[0]))
.then((details) => {
console.log(details);
})
.catch((err) => {
console.error(err);
});
Depending on how the rest of your code is structured, you need to determine when to pass a specific listing. In my app that I built with this package, I make the initial search request that returns all the listings, and then when the user clicks on a listing for a search term, I make another request passing along that specific listing OBJECT and then it returns the details.
To be even more specific ...
On the client side the user searches, I emit that to the server then make the search request.
Once the request is finished I emit the results back to the client and then display the information to the user and store each listing object inside a custom data attribute inside each listing for later use.
Once the user clicks on a specific listing, I grab the object from the data-attribute and emit that to the server.
The server listens for that and makes the second DETAILS request using that listing object which is then emitted back to the clients browser.
I am using google cloud functions to register push notifications through firebase. In my app, i have a notifications reference that changes for a current user whenever they get a new follower or like, etc. As of right now I am able to send the notification to the phone whenever that whole reference child changes
For example, if any single post is liked, then it will send a notification. What I need to do is observe the current user to only send the notification that single person.
Here is my JavaScript file
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendPushNotification = functions.database.ref('/notification/{id}').onWrite(event => {
const payload = {
notification: {
title: 'New message arrived',
body: 'come check it',
badge: '1',
sound: 'default',
}
};
return admin.database().ref('fcmToken').once('value').then(allToken => {
if (allToken.val()) {
const token = Object.keys(allToken.val());
return admin.messaging().sendToDevice(token, payload).then(response => {
});
}
});
});
I would like to replace this line:
functions.database.ref('/notification/{id}').onWrite(event => {
With this:
functions.database.ref('/notification/{id}').(The current user ID).onWrite(event => {
How do I get the current users id?
You seem very new to JavaScript (calling it JSON is sort-of a give-away for that). Cloud Functions for Firebase is not the best way to learn JavaScript. I recommend first reading the Firebase documentation for Web developers and/or taking the Firebase codelab for Web developer. They cover many basic JavaScript, Web and Firebase interactions. After those you'll be much better equipped to write code for Cloud Functions too.
Now back to your question: there is no concept of a "current user" in Cloud Functions. Your JavaScript code runs on a server, and all users can trigger the code by writing to the database.
You can figure out what user triggered the function, but that too isn't what you want here. The user who triggered the notification is not the one who needs to receive the message. What you want instead is to read the user who is the target of the notification.
One way to do this is to read it from the database path that triggered the function. If you keep the notifications per user in the database like this:
user_notifications
$uid
notification1: ...
notification2: ...
You can trigger the Cloud Function like this:
exports.sendPushNotification = functions.database.ref('/user_notification/{uid}/{id}').onWrite(event => {
And then in the code of that function, get the UID of the user with:
var uid = event.params.uid;
For Swift 3.0 - 4.0
You can do this:
import Firebase
import FirebaseAuth
class YourClass {
let user = Auth.auth().currentUser
let userID = user.uid
// user userID anywhere
}