How can I automatically assign a role to a CouchDB user? - node.js

I have the following method for registering users:
// Registration method
exports.register = function(req, res) {
var username = req.body.username,
password = req.body.password,
first = req.body.first;
last = req.body.last;
role = req.body.institution;
nano = require('nano')('http://127.0.0.1:5984');
var users = nano.use('_users');
var user = {
"_id": "org.couchdb.user:" + username,
"name": username,
"type": "user",
"roles": [],
"password": password,
"realname": first + " " + last
};
users.insert(user, function(err, body) {
if(err) {
res.send(err.reason, err.status_code);
} else {
res.send(body, 200);
}
});
};
As you can see, I have a variable called role which I would like to set in the "roles" array. Unfortunately, as you probably know, CouchDB only allows the admin to set roles. I'm okay with this, but I'm using the roles as institutions, so I'd like a registering user to be able to pick what institution they're registering under and therefore it should automatically be assigning them this role. Without hard-coding my admin credentials into this method, i.e.
nano.config.url = "http://admin:password#127.0.0.1:5984/"
How can I automatically assign a new user to their specified role as I've mentioned?

The roles array is used by CouchDB to decide database authorization. This allows you to configure access to a given database by a user's role. If the user can set their own role then somebody can give themselves access to any institution. They can even make themselves an admin, which defeats the purpose of protecting your admin password.
I'm not sure what your use-case is, but I don't think you are trying to prevent access to other institutions. Rather, I suspect you are trying to use the roles to perform some other application function (e.g. filtering out information for them to simplify what they see). Since you are wanting to perform a non-security function, I strongly suggest that you just add your own field to the users documents. You could add an institutions property to each user.
If you really want to do this, there is a way. The error you are receiving is coming from an update validation function in _design/_auth in the _users database. As an admin you can change this function.
Download the document. Open the file and search for Only _admin may edit roles to see where the test is being performed. Remove that conditional and save the document back to CouchDB.

Related

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

Azure add user authentication method using Microsoft.Graph.GraphServiceClient

I'm creating new user using GraphServiceClient with SignInType as userName something like this:
var user = new User
{
AccountEnabled = true,
GivenName = "Joe",
Surname = "Bob",
DisplayName = "Name",
Mail = "joe#bob.com",
MobilePhone = "111111111",
Identities = new List<ObjectIdentity>()
{
new ObjectIdentity
{
SignInType = "userName",
Issuer = "<Issuer...>",
IssuerAssignedId = "someName"
},
},
PasswordProfile = new PasswordProfile
{
ForceChangePasswordNextSignIn = false,
Password = "<pass>"
},
PasswordPolicies = "DisablePasswordExpiration,DisableStrongPassword",
};
await gsc.Users.Request().AddAsync(user);
After that user is created in Azure and I can use this account for login into app that is using B2C SignIn policy. But when I want invoke ResetPassword policy for that user, after successfully email verification, I'm getting error: "An account could not be found for the provided user ID.". After some trial and error I've figure out that there is missing data in azure user profile tab "Authentication methods" When I fill out input for email everything is working fine and user is able to reset password.
So I'm searching for a way how to populate user email into this section "Authentication methods" during creation time.
In this circumstance, where you want to create users via Graph API, you cannot populate the strongAuthenticationEmail attribute usng Graph API. This attribute holds the email associated with a username based account for password reset operations. User Flows will read this attribute during password reset. Therefore this wont work.
You need to use a custom policy for a username based journey and replace all instances of strongAuthenticationEmail with extension_whatever. Then in your Graph API payload, insert the email address in a key called extension_GUID_whatever.
https://github.com/azure-ad-b2c/samples/tree/master/policies/username-signup-or-signin

FeathersJS way of preventing duplicates in a service

I'm using FeathersJS and MongoDB to develop an app. I want to prevent some services to create duplicates of some values (or pairs of values).
For example, the FeathersJS "Authenticate" service created with the feathers-cli tool doesn't prevent the app from creating 2 or more users with the same email (at least using MongoDB). Another example would be a service to create some "categories" for each user. I want that the backend prevents a user to create 2 or more categories with the same name, but I need to allow 2 different users to create their own categories although their names are the same (but not the users).
I know I can do this by using indexes in the MongoDB collections, but this would make the app MongoDB dependant.
Is there someone that knows if there's any kind of hook or whatever that is the recommended way to do such things "the FeathersJS way"?
Thank you!
In most cases uniqueness can - and should - be insured at the database or ORM/ODM level since it will give you the best performance (something that in most cases isn't worth sacrificing for portability).
A more Feathers-y way and to accomplish more complex restrictions would be Feathers hooks which are an important part of Feathers and explained in detail in the basics guide.
In this case, a before hook could query the total of items and throw an error if there are any:
const { Conflict } = require('#feathersjs/errors');
app.service('myservice').hooks({
before: {
create: [async context => {
const { fieldA, fieldB } = context.data;
// Request a page with no data and extract `page.total`
const { total } = await context.service.find({
query: {
fieldA,
fieldB,
$limit: 0
}
});
if(total > 0) {
throw new Conflict('Unique fields fieldA and fieldB already exist');
}
return context;
}]
}
})

SailsJS. Generic policy which check if groupID of element in request is the same as user's group

I have big API and I'm using blueprints as it is really convenient for most of operations.
It is multi-tenant application, so every element has groupID field.
I need to have policy which will check if element which is going to be edited/deleted belongs to user's group.
I have user's groupID in request already injected by JWT policy,
but how can I make policy to generic policy which will check appropriate model for id and compare it with user id ?
It can't be done by groupID from request as hacker can use his JWT token and put his groupID in request but access not his element.
IS it possible or I have to create separate policy per model?
I found it.
You can get modelName from req.options.model when you are using Blueprints.
Unfortunately you can't use this[modelName] as option is giving you model name starting with small letter, so first you have to upper case first letter with e.g. var modelName = req.options.model.charAt(0).toUpperCase() + req.options.model.slice(1);
and then you are free to use this[modelName].whateverYouNeed
I used it for generic policy to let user editing only his own group elements.
var modelName = req.options.model.charAt(0).toUpperCase() + req.options.model.slice(1)
var elementID = null
if (req.params.id) { // To handle DELETE, PUT
elementID = req.params.id
}
if (req.body.id) { // To handle POST
elementID = req.body.id
}
this[modelName].findOne({
id: elementID
}).exec(function(err, contextElement) {
if(err) {
return res.serverError(err)
}
if(contextElement.group=== req.user.group.id) {
sails.log('accessing own: ' + modelName)
return next()
}
else {
return res.forbidden('Tried to access not owned object')
}
})

Resources