Fluid-framework Audience and member profile data - fluid-framework

I there a way to atach payload data to a member (myself) in the Audience. I have some client profile data which is better if kept in sync with user presence reflected by the Audience object. sort of like:
const clientId = audience.getMyself().connections[0].id;
audience.setMemberData(clientId, user.profile);
I know I can definitely create a SharedMap for that, but it seems like a waste maintaining a separate DDS when there's already the Audience DDS w/ its Members.

You can pass additional user data through the audience.
const userDetails: ICustomUserDetails = {
email: "xyz#outlook.com",
address: "Redmond",
};
const config = {
tenantId: "myTenantId",
tokenProvider: new AzureFunctionTokenProvider(
"myAzureFunctionUrl" + "/api/GetAzureToken",
{ userId: "UserId", userName: "Test User", additionalDetails: userDetails }
),
orderer: "https://myOrdererUrl",
storage: "https://myStorageUrl",
};
See https://learn.microsoft.com/en-us/azure/azure-fluid-relay/how-tos/connect-fluid-azure-service#adding-custom-data-to-tokens for more details.

Related

How do you omit fields in TypegraphQL

Let's say we have the following User model.
{ id: ID, email: string, username: string }
Then I want to define 2 queries:
Used by the owner to get their settings page so it contains sensitive information such as email (perhaps sin number)
Used by other users to search for a user (by username) & we do NOT want to expose the email or sin number
I have been researching the documentation & cannot find how to accomplish this. I was thinking to grab the info for the fields manually & parse it per query but that seems like an oversight.
UPDATE:
Here is sort of what I am trying to do:
class User {
#Field(
() => ID
)
id: string;
#Authorized("CURRENT_USER")
#Field(
{
nullable: true
}
)
email: string;
#Field()
username: string;
}
Resolver:
export default class UserResolver {
#Authorized("CURRENT_USER")
#Query(
() => User
)
async user(#Arg('username', () => String) username: string) {
// TODO: if username is current user then allow email
// else do not allow email, (I need auth checker in here)
}
}
If I understand your question correctly you should be able to use the Authorized decorator in TypegraphQL. With this solution you should be able to add it to the email field in your User model. This should also be able to work with the sid field as well
Have a look here: https://typegraphql.com/docs/authorization.html
For example your User model could look something like this:
class User {
#Field()
id: ID;
#Authorized("LOGGEDINUSER")
#Field({nullable: true})
email: string;
#Field()
username: string;
}
You will have to allow the email field to be nullable
You will also need to define an authChecker, with this you can run your logic to check if the user is the owner of the data, therefore granting them access to the data.
An authChecker can look something like this:
export const customAuthChecker: AuthChecker<Context> = (
{ args, context, info, root },
roles
) => {
// roles is an array of string which contains the authorization required to access the current resource
// this is specified in the #Authorized decorator
if (roles.includes("LOGGEDINUSER")) {
// here check if the user is actually logged in and if they are allowed to access the resource
// return true if they are allowed to access the resource
}
return false;
};
You will also need to change your call to the buildSchema to include the custom authChecker and authMode.
For example:
const schema = await buildSchema({
resolvers: [UserResolver],
authChecker: customAuthChecker,
authMode: "null",
});
Note this will still return an email field but instead of returning the actual email it will return null when the user does not meet the authentication requirements

How can I specify which Google Wallet pass is for a specific user?

I'm currently working on building some loyalty cards via Google wallet api.
Creating and distributing passes is not a problem, but I'm not so sure on how to specify one card to one user.
I understand you are able to set the MultipleDevicesAndHoldersAllowedStatus property to one user. I also understand that you are supposed to set a unique userId which can be the user's email_address, but this does not necessary mean that only this user with this email_address is able to get the pass.
How can I make sure that a user with user1#mail.com is only able to install a specific pass?
I created a sample pass object with my personal email address as the userId; however, I was able to use the save link created by
const claims = {
iss: credentials.client_email,
aud: "google",
origins: ["www.example.com"],
typ: "savetowallet",
payload: {
loyaltyObjects: [
{
id: objectId,
},
],
},
};
const token = jwt.sign(claims, credentials.private_key, {
algorithm: "RS256",
});
const saveUrl = `https://pay.google.com/gp/v/save/${token}`;
to save it to a device that was not logged in w/ my personal email.
Is there a way for the pass (or maybe the save url?) to check again for the proper gmail address before letting the user download the pass to their wallet?
(I had to use android-pay since i don't have enough reputation to tag it with google-wallet-api)

Exclude user's password from query with Prisma 2

Recently I started working on a new project to learn some new technologies (Prisma 2, REST api with Express, etc.). Tho, I faced a problem.
My app has a user authentication system and the user model has a password column. So, when the client requests a user, the backend selects all the columns from the database including the password (that's hashed by the way).
I tried to not select the password column on the prisma findMany, like this:
await prisma.user.findUnique({
where: {
...
},
select: {
password: false
}
});
But I got an error by prisma saying that the select should contain at least one truly value. Thus, I added id: true to the select. I made an api request and I saw that only the id was returning for the user.
By my understanding, prisma expects me to add all the columns I care to the select object. But, I need a lot of columns from the user and I am making a lot of queries to fetch users and I cannot just write all the field I need everytime.
So, I wanted to ask you if there is a legit way to do that.
PS: I don't take "use rawQuery instead" as a solution.
The only legit way is adding column: true to the columns you want to include. There are requests for excluding columns here so it would be great if you could add a 👍 to the request relevant to you so that we can look at the priority.
https://github.com/prisma/prisma/issues/5042
https://github.com/prisma/prisma/issues/7380
https://github.com/prisma/prisma/issues/3796
I've been wondering about how to implement this as well, and bafflingly the issues linked in #Ryan's post are over two years old, and still unresolved. I came up with a temporary workaround, which is to implement a middleware function for the Prisma client which removes the password field manually after each call.
import { PrismaClient } from '#prisma/client'
async function excludePasswordMiddleware(params, next) {
const result = await next(params)
if (params?.model === 'User' && params?.args?.select?.password !== true) {
delete result.password
}
return result
}
const prisma = new PrismaClient()
prisma.$use(excludePasswordMiddlware)
This will check if the model being queried is a User, and it will not delete the field if you explicitly include the password using a select query. This should allow you to still get the password when needed, like when you need to authenticate a user who is signing in:
async validateUser(email: string, password: string) {
const user = await this.prisma.user.findUnique({
where: { email },
select: {
emailVerified: true,
password: true,
},
})
// Continue to validate user, compare passwords, etc.
return isValid
}
Check out the following code
Exclude keys from user
function exclude(user, ...keys) {
for (let key of keys) {
delete user[key]
}
return user
}
function main() {
const user = await prisma.user.findUnique({ where: 1 })
const userWithoutPassword = exclude(user, 'password')
}
reference
prima official Website

How to use ramda mergeRight in Typescript and respect interface definitions

I'm trying to make a copy of an object and change properties using Rambda's mergeRight function. The problem is it allows me to merge in properties that do not exist in the interface definition.
import {mergeRight} from "ramda";
export interface User {
readonly userId: string
readonly username: string
}
const user: User = {
userId: "12345",
username: "SomeUser"
}
//I want this to be a compile time error, because "something" is not a property of User interface
const updatedUser: User = mergeRight(user, {something: "3"})
Is there any way I can ensure that the properties I am merging are part of the User type, without having to specify an entire new User object (thus defeating the advantage of mergeRight)? This would prevent a simple typo from causing a runtime error that is difficult to debug.
Ideally I would like Typescript to detect this at compile time
To filter out keys that are not part of user, use R.pick to take just keys that exist in User from the new object.
This will only effect the root level of the object, and not deeper mismatches.
const { pick, keys, mergeDeepRight } = R
const user = {
userId: "12345",
username: "SomeUser"
}
const getUserKeys = pick(keys(user))
//I want this to be an error, because "something" is not a property of User interface
const updatedUser = mergeDeepRight(user, getUserKeys({
something: "3"
}))
console.log(updatedUser)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
It seems that simply casting the anonymous object as User will give the error I want. That's good enough for my use case.
//This causes a compile time error
const updatedUser: User = mergeRight(user, {something: "3"} as User)
//This does not
const updatedUser2: User = mergeRight(user, {userId: "3"} as User)

Merge two endpoint using a different name and schema with Eve

This is a simplified version of my settings.py:
accounts_schema = {
'username': {
},
'password': {
}
}
accounts = {
'schema': accounts_schema,
}
After a user is created with a POST request to the /accounts endpoint, the user's info can be retrived with a GET to /accounts/<id_of_user>.
I would like to know if it possible to "merge" two endpoints that are using a different schema so
POST /update_accounts/<id_of_user>
will point to
/accounts/<id_of_user>
but update_accounts must have, for example, this schema:
update_accounts_schema = {
'token': {
},
'validity': {
}
}
From the documentation:
Multiple API endpoints can target the same database collection. For example you can set both admins and /users to read and write from the same people collection on the database.
So you can have update_accounts and accounts both targeting the same datasource, and each one with its own user privileges/allowed methods, etc.

Resources