Is it possible to use request header as a parameter inside FaunaDB user defined function (UDF)? - header

Can I get the header's value somehow inside the UDF's body?
The idea is to implement our own ABAC based on custom header parameters (like userId, role, key, UDF, etc.).

Fauna queries do not have access to request headers.

You can use Fauna to generate Tokens that you can use in your headers for querying data. While creating user you will create a username/email and a password that will be stored as a "credential". You can create a login UDF and a user_by_email index which will verify user and issue a token.
The login UDF looks something like this:
Query(
Lambda(
["data"],
Create(Collection("User"), {
credentials: { password: Select("password", Var("data")) },
data: {
firstName: Select("firstName", Var("data")),
lastName: Select("lastName", Var("data")),
email: Select("email", Var("data")),
role: Select("role", Var("data")),
phone: Select("phone", Var("data")),
}
})
)
)
And the user_to_email index will look like this:
Source Collection: User
Index Name: user_by_email
Terms: data.email
Values: Serialized: true
Here is a link to Fauna's User Authentication document: https://docs.fauna.com/fauna/current/tutorials/authentication/user.html
Hope this helps.

Related

Reddis Stack JSON: Nested JSON vs namespace usage recommendation

The redisinsight workbench use namespaces to store JSON objects, such as:
school_json:1 -> {...}
school_json:2 -> {...}
...
But I am asking myself if that is the way to go when dealing with JSON documents. The JSON examples at https://redis.io/docs/stack/json/path/ showcase how to store items in a nested JSON object called store.
In my case I would like to store users. At first I had a structure where a toplevel key users exists such as
users -> {
1: { // actually I'm using a uuid here
username: "Peter"
email: ... // etc.
},
2: {
username: "Marie",
email: ...
}
}
Or should I use namespaces here as well which would look somewhat like:
users:1 -> {
username: "Peter"
email: ...
},
users:2 -> {
username: "Marie",
email: ...
}
I assume that using namespaces would have performance benefits over nested JSON but the example in the redis documentation which uses a nested JSON object to store several items confused me if that is actually true.
I found this answer but that is discussing redis, not redis stack using JSON (which may come with other optimizations).
Thanks in advance!

Best way to return different API response based on user role (Prisma/NestJS)

I’m developing a REST API using NestJS and Prisma. The RBAC is done using an enum with a role guard.
I would like to use the same controller method for different user roles and return specific fields only for specific roles.
What’s the best way to do this?
Like ADMIN role should return all fields while USER role should only return certain fields.
Thanks.
If it's possible in your case, you can describe your select opition in prisma request based on RBAC
const getUser: object | null = await prisma.user.findUnique({
where: {
id: 22,
},
select: {
email: role === Role.Admin, // <== like this
name: [Role.Admin, Role.User].includes(role), // <== or like this with multiple roles
},
})

GraphQL best practice on redacting fields that require authorization

Consider a User type, with an email field that, for certain "anonymous" users, should only be accessible if the request was properly authorized. Other users are fine with having their email publicly displayed.
type User {
name: String!
"Anonymous users have the email only accessible to admins"
email: String!
}
What are the pros and cons of these two approaches for handling that field in the email resolver for anonymous users?
throw if the request is unauthorized. In that case,
email needs to be declared as nullable String instead of String!, or the entire query will error.
The client may want to match the errors to the data. Because non-anonymous users will have their email accessible without authorization, errors and data may have different numbers of elements, so this matching seems impossible, at least with apollo-server, which doesn't return anything in each errors element that would indicate to which user it belongs.
email will be misleadingly null for anonymous users, confusing the situation with that of the email never having been added in the first place. Remember that email needs to be String, not String!, so a null email is reasonable.
There is a clear errors array in the response, so this feels like the "proper" approach?
return the email in a redacted form, e.g [NOT AUTHORIZED TO ACCESS THIS USER'S EMAIL].
This keeps the objects intact with clear errors for sensitive emails, instead of misleading "null" emails.
email can stay non-nullable
No need to try to match errors with data.
There is no explicit errors array in the response, so this feels like a hack.
Note that for arrays, returning a REDACTED form is not an option, because the query may ask for a field within the array (e.g. { anonUsers { email } }). The only option there is to return [] (directly or by throwing).
Am I missing anything? Is there prior work on this topic? How can I make a decision?
I've done something similar recently, what seemed to work best was to create an interface for the base user type.
interface UserInformation {
id: ID!
userName: String
firstName: String!
lastName: String
avatarImage: String
city: String
...
}
Then you'd have two separate implementations of it:
type UserPublic implements UserInformation {
id: ID!
userName: String
firstName: String!
lastName: String
avatarImage: String
...
}
type UserPrivate implements UserInformation {
id: ID!
userName: String
firstName: String!
lastName: String
avatarImage: String
friends: [UserInformation!]
pendingFriends: [UserInformation!]
phone: String
email: String
birthday: DateTime
...
}
All your other queries and types use the UserInformation base interface type when exposing Users. Then your graphql server simply returns either UserPublic or UserPrivate depending on what type of access the user has.
On the client side you query it like this:
query SomeQuery {
getSomeData {
user {
id
userName
firstName
lastName
avatarImage
...on UserPrivate {
email
phone
}
...
You can then check client-side whether the returned type of a field (__typename) was UserPublic or UserPrivate and act accordingly.

How and where can I create a getUserById function?

I'm trying to find a way to get users in strapi by ID. But I don't have access to the User identity in the controller.
I have tried go write a function in the controller and service both diedn't work cause I cant access the find or findOne method
getById: async(ctx) => {
console.log('from user controller', ctx);
return User.find({ _id: ctx.request.body.id })
}
I reckon you are using typeorm? and User is your User repository?
So in that case a small change should do
getById: async(ctx) => {
console.log('from user controller', ctx);
return User.find({ where: {_id: ctx.request.body.id} })
}
(Also make sure you have sanitized your input somewhere)

Meteor.loginWithPassword callback doesn't provide custom object in User accounts doc

Meteors loginWithPassword() function doesn't provide me the object systemData, which I adding to user doc (not to profile obj) during registration. The thing is, that if I look into console after logging in, I can see that object systemData (that means probably it's not publish issue), but not in callback of loginWithPassword() function, where I need them (to dynamically redirect user to proper page). Is there way to get this object, without any ugly things like timers?
Meteor.loginWithPassword(email, password, function(errorObject) {
if (errorObject) {
...
} else {
// returns true
if (Meteor.userId()) {
// returns false
if (Meteor.user().systemData) {
...
}
// user doc without systemData object
console.log(JSON.stringify(Meteor.user());
}
}
I've adding object systemData on creating user:
Accounts.onCreateUser(function(options, user) {
if (options.profile) {
user.profile = options.profile;
}
...
user.systemData = systemDataRegularUser;
return user;
});
Are you sure publish data to Client ?
I get User Info Using loginWithPassword in callback function.
Meteor.loginWithPassword username,password,(error,result1)->
options =
username: username
password: password
email: result['data']['email']
profile:
name: result['data']['display-name']
roles: result.roles
console.log Meteor.user(), result1
I Create user flowing code: (options contains systemData)
Accounts.createUser option
The first problem is that you want a custom field on a user document published to the client. This is a common question - see the answer here.
The next problem is that even after you add something like:
Meteor.publish("userData", function () {
return Meteor.users.find(this.userId, {fields: {systemData: 1}});
});
I think you still have a race condition. When you call loginWithPassword, the server will publish your user document, but it will also publish another version of the same document with the systemData field. You are hoping that both events have completed by the time Meteor.user() is called. In practice this may just work, but I'm not sure there is any guarantee that it always will. As you suggested, if you added a slight delay with a timer that would probably work but it's an ugly hack.
Alternatively, can you just add systemData to the user's profile so it will always be published?
I didn't find exact way how to solve this, but found easy workaround.
To make some action right after user logged in (eg. dynamically redirect user to proper page), you can hook on your home page with Iron router.(If you using it.) :
this.route('UsersListingHome', {
path: '/',
template: 'UsersListing',
data: function() { ... },
before: function() {
if (isCurrentUserAdmin() && Session.get('adminJustLogged') !== 'loggedIn') {
Router.go('/page-to-redirect');
Session.set('adminJustLogged','loggedIn');
}
}
});
After click on logout of course if (isCurrentUserAdmin()) { Session.set('adminJustLogged', null); }
I've further thought about calling Meteor.call('someMethod') to fetch userData object in Method callback, but now I'm satisfied.
PS: I know that it's not recommended to have plenty session variables or other reactive data source for speeding-up my app, but I believe, that one is not tragedy :)
PS2: Anyway, thanks for your answers.