Permission for all users to post to a specific feed - getstream-io

I have been attempting to implement stream via react native and seem to be struggling with what looks like a permissions issue in relation to all users being unable to post to another 'entities' feed. My use case is essentially several 'topic' pages, where all users of my app can post to and comment etc. The topic page will also have an owner who can manage the page if needs be, so they too are essentially a 'user.
I've read elsewhere that global write permissions are not enabled by default so may need a member of the stream team to look at this for me if possible.
Here is the code I'm trying to use on the client side:
let user = client.feed('user', "bob");
let activity = {
actor: "bob",
verb: 'post',
object: "Hello world",
foreign_id: 'post:1',
to: ['user:topic-page-1'],
};
user
.addActivity(activity)
.then(data => {
console.log('success');
})
.catch(reason => {
alert(reason);
});
Another small issue I can't seem to get to the bottom of, is even when posting to the same user's timeline, the post username is always displaying as "Unknown" no matter what data I attach. What am I getting wrong here please?

For the actor field to be treated as a user entity, you should use a reference, something like client.currentUser or client.user('bob'). This should fix your post username issue.

Related

How to transfer ownership of file create with service account using Drive API v3

I'm using the googleapis package in npmjs to create a spreadsheet as follows:
const { google } = require("googleapis")
const sheets = google.sheets("v4")
const drive = google.drive("v3")
async function run() {
// ....
// auth obtained here
// ....
var resp = await sheets.spreadsheets.create({
auth,
resource: {
properties: {
title:"SSTEST2",
}
}
})
var folder = "1XXXXXXXXXXXXXXXXXXXXX" // actual folder id omitted
var ssid = resp.data.spreadsheetId
resp = await drive.files.update({
auth,
addParents:[folder],
removeParents:"root",
fileId:ssid
})
}
I'm using a service account I created, so as a result when the process is complete, the spreadsheet is owned by googlesheetsuser#some-random-words.iam.gserviceaccount.com. This doesn't seem to prevent me from editing the document, however if I delete it, presumably it is still sitting off in limbo somewhere (if I try to access it before moving it to my folder I get a page telling me to request access, which doesn't make any sense because a service account address isn't a real e-mail and so there's no way for this to succeed!) so I'm not sure if it's even going into a trash can for the service account or if it will sit around on Google's servers forever. Not actually owning the document causes issues so I need to get ownership.
On the other hand, I haven't found any documentation that explains exactly what I need to do to transfer ownership to myself. Google's API seem to leave out a lot of information about exactly what to pass; leaving out important info in the sample source with comments like "TODO: Add desired properties to the request body" is beyond unhelpful! I have found a few examples in other languages using other mechanisms I don't recognize which purport to do at least something close to this, but I haven't been able to glean any useful information about how to do it in my particular setup. I have also found a couple of answers which seem to imply that doing this is impossible as you can't change ownership between domains, in which case I just have no words because obviously leaving it owned by my service account creates issues and leaving it owned by the service account indefinitely just isn't an option so some solution is needed.
How can I wind up with my file (Google Spreadsheet) owned by myself at the end of this process?
Update: Per the suggested by #Kessy, I tried to use this code:
const resp = await drive.permissions.update({
auth,
fileId: <fileId>,
permissionId: <id found using list call>,
transferOwnership: true,
requestBody: {
role: "owner",
emailAddress: <my email address>,
type: "user",
}
})
I get this error:
Error: The resource body includes fields which are not directly writable.
I don't know which field it thinks isn't writeable. I tried omitting "type" and get the same error. I tried omitting "role" and it complains that this field is required: Error: The permission role field is required. If I omit "emailAddress" then I don't get the error, but this defeats who whole purpose of the call, which is to transfer ownership to that e-mail address!

What would be the most proper way to send email?

I'm learning TypeOrm and i'm trying to implement an email verification system after a user creates an account.
Let's say i have two entities, User and EmailVerification. When the user is created, an EmailVerification related to this user is inserted in the database. The next step would be to send an email to this user right after the EmailVerification is created.
But i'm not sure about what typeOrm feature to use to call my email service send function.
I was thinking of two ways to achieve this,
1 - From the transaction as a complementary step after inserting user and emailVericiation in database :
await getManager().transaction(async entityManager => {
await entityManager.save(user);
await entityManager.save(emailVerification);
// send the message directly from the transaction right after the user and emailVerification is created
await emailService.send(message);
})
2 - From an EntitySubscriber right after the creation of the EmailEntity :
#EventSubscriber()
export class EmailVerificationSubscriber implements EntitySubscriberInterface<EmailVerification> {
#AfterInsert()
sendEmail() {
// ... //
// get related user email
// ... //
// then send the message
await emailService.send(message);
}
}
These two ways seems sufficient to me, but i would like to know if there is some kind of best practice for this use case ?
I can provide more informations if needed
It doesn't matter very much which option you choose.
That being said, the email you send is part of a workflow that starts when you insert your emailVerification object. So it makes sense to associate it with that operation.
If you use a similar workflow in future for password recovery, it will become obvious why that makes sense.

Is there anyway to get the "watermark" value in the bot builder?

I have a webchat for a user connected to the bot through directline.
I want a second user to join to the same conversation, but I want the second user to be able to read the full conversation.
Right now when the second user connects to the conversation it doesn't see anything of the first user conversation because he doesn't join with a watermark value.
I have this code on bot builder v4 right now:
const options = {
method: 'GET',
uri: 'https://myuri/addRow?conversationId='+stepContext.context.activity.conversation.id,
};
await req-promise(options);
I would like to send something like this:
const options = {
method: 'GET',
uri: 'https://myuri/addRow?conversationId='+stepContext.context.activity.conversation.id+'watermark='+watermark,
};
await req-promise(options);
Is there anyway to get that watermark value?
Thanks
Per this GitHub issue.
The cache of messages in the Direct Line connector service is intended to be used as a connection reliability mechanism, not as an actual message history store.
If you require more granular control over conversation history, you will need implement an a transcript store server side. And, you can use the SendConversationHistoryAsync api to send chunks of history messages to the conversation.
We do not currently have a complete example demonstrating this, but it is in the works.
I would recommend using a transcript logger to store and manage your own conversation history instead of trying to pull the messages from the cache. Also, if you try to use the watermark, you'll run into permission issues since one conversation doesn't have the ability to see another conversation's data.
Hope this helps!

Getting the user id from a Firestore Trigger in Cloud Functions for Firebase?

In the example bellow, is there a way to get the user id (uid) of the user who wrote to 'offers/{offerId}'? I tried to do as described here but it doesn't work in Firestore.
exports.onNewOffer = functions.firestore
.document('offers/{offerId}')
.onCreate(event => {
...
});
I was struggling on this for a while and finally contacted the firebase Support:
The event.auth.uid is undefined in the event object for firestore database triggers. (It works for the realtime Database Triggers)
When I console.log(event) I can’t find any auth in the output.
The official support answer:
Sorry the auth is not yet added in the Firestore SDK. We have it listed in the next features.
Keep an eye out on our release notes for any further updates.
I hope this saves someone a few hours.
UPDATE:
The issue has been closed and the feature will never be implemeted:
Hi there again everyone - another update. It has been decided that unfortunately native support for context.auth for Firestore triggers will not be implemented due to technical constraints. However, there is a different solution in the works that hopefully will satisfy your use case, but I cannot share details. On this forum we generally keep open only issues that can be solved inside the functions SDK itself - I've kept this one open since it seemed important and I wanted to provide some updates on the internal bugs tracking this work. Now that a decision has been reached, I'm going to close this out. Thanks again for everyone's patience and I'm sorry I don't have better news. Please use the workaround referenced in here.
Summary of how I solved this / a workable solution:
On client
Add logged in/current user's uid (e.g. as creatorId) to entity they're creating. Access this uid by storing the firebase.auth().onAuthStateChanged() User object in your app state.
In Firebase Firestore/Database
Add a Security Rule to create to validate that the client-supplied creatorId value is the same as the authenticated user's uid; Now you know the client isn't spoofing the creatorId and can trust this value elsewhere.
e.g.
match /entity/{entityId} {
allow create: if madeBySelf();
}
function madeBySelf() {
return request.auth.uid == request.resource.data.creatorId;
}
In Firebase Functions
Add an onCreate trigger to your created entity type to use the client-supplied, and now validated, creatorId to look up the creating user's profile info, and associate/append this info to the new entity doc.
This can be accomplished by:
Creating a users collection and individual user documents when new accounts are created, and populating the new user doc with app-useful fields (e.g. displayName). This is required because the fields exposed by the Firebase Authentication system are insufficient for consumer app uses (e.g., displayName and avatarURL are not exposed) so you can't just rely on looking up the creating user's info that way.
e.g. (using ES6)
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
const APP = admin.initializeApp()
export const createUserRecord = functions.auth.user()
.onCreate(async (userRecord, context) => {
const userDoc = {
id: userRecord.uid,
displayName: userRecord.displayName || "No Name",
avatarURL: userRecord.photoURL || '',
}
return APP.firestore().collection('users').doc(userRecord.uid).set(userDoc)
})
Now that you have a validated creatorId value, and useful user objects, add an onCreate trigger to your entity type (or all your created entities) to look up the creating user's info and append it to the created object.
export const addCreatorToDatabaseEntry = functions.firestore
.document('<your entity type here>/{entityId}')
.onCreate(async (snapshot, context) => {
const userDoc = await APP.firestore().collection('users').doc(snapshot.data().creatorId).get()
return snapshot.ref.set({ creator: userDoc.data() }, { merge: true })
})
This clearly leads to a lot of duplicated user info data throughout your system -- and there's a bit of clean up you can do ('creatorId` is duplicated on the created entity in the above implementation) -- but now it's super easy to show who created what throughout your app, and appears to be 'the Firebase way'.
Hope this helps. I've found Firebase to be super amazing in some ways, and make some normally easy things (like this) harder than they 'should' be; on balance though am a major fan.
The documentation states clearly that the context.auth param is only available in the Realtime Database.
This field is only populated for Realtime Database triggers and
Callable functions. For an unauthenticated user, this field is null.
For Firebase admin users and event types that do not provide user
information, this field does not exist.
Personally I realized that I had the userId already in the path of my data.
export const onCreate = functions.firestore.document('docs/{userId}/docs/{docId}')
.onCreate((snapshot, context) => {
const userId = context.params.userId;
Until this is added to firestore functions a workaround is to add the user_id as a field when creating a document then deleting after. You can then grab it in the function onCreate then after you use it for what you need it for, while still in the function, just delete the field from that document.
As already suggested above, the workaround will be to add a user_id field with the data itself and then read it on the server.
The drawback with this approach will be a security loophole. As we are not verifying the user id on the server, any other user will be able to impersonate other users by sending their id with the data.
For security critical applications, the solution for this will be to use security rules to verify that the correct user_id has been sent with the data
allow write: if resource.data.user_id == request.auth.uid;
You could add your data via Callable function, from which you can access current user id:
exports.addNewOffer = functions.https.onCall(async (data, context) => {
const uid = context.auth.uid
const writeResult = await admin.firestore().collection('offers').add({ ... })
...
return { id: writeResult.id, ... }
})
What is about snap._fieldsProto.uid.stringValue
Example:
exports.hello = functions.firestore.document('hello/{worldId}').onCreate((snap, context) => {
console.log(snap._fieldsProto.uid.stringValue)
});
This should do the trick:
exports.newMessage = functions.firestore
.document('groups/messages/{messageId}')
.onCreate((snap, context) => {
var fromId = snap.data().fromId;
You can get the current signed-in user tokenId by calling getIdToken() on the User: https://firebase.google.com/docs/reference/functions/functions.auth.UserInfo

Is there a library compatible with Hapi for fine-grained ACL / User permissions?

Looking to use HapiJS as our API server. We need fine-grained user permissions, e.g. "User A can edit field B" "User C can view field D" for a given model / resource.
Before we start building something I've been looking to see if something like this has already been done that is compatible with Hapi.
I have just read an article where the ACL permissions are validated using the build-in scopes.
Here is the link to the mentioned article :
https://blog.andyet.com/2015/06/16/harnessing-hapi-scopes/
And to resume quickly (using the example from the above link), you get a user object that looks like so :
{
"username": "han",
"scope": ["door-trash-compactor"]
}
The scope can be generated by whatever is backing your ACL for this user. In this case you have the resource door with id trash-compactor that can be checked like so :
server.route({
method: 'GET',
route: '/doors/{door_id}',
config: {
handler: function (request, reply) {
reply(request.params.door_id ' door is closed');
},
auth: {
scope: ['door-{params.door_id}']
}
}
});
The scope door-{params.door_id} will be translated to door-trash-compactor which will then be validated. Han's request to the trash compactor door will be valid and he will get the door is closed message.
The blog post is well written (much better then this summary) and describes this in better detail - would recommend the read.
I've recently been working on an ACL project for hapijs. It should get you a good start. https://www.npmjs.org/package/hapi-authorization

Resources