I am using express-validator to validate my request data. According to the docs to add them we have to declare them like this
app.use(expressValidator({
customValidators: {
isArray: function(value) {
return Array.isArray(value);
},
gte: function(param, num) {
return param >= num;
}
}
}));
I added a validator to it like the docs. but I can only return true or false.
I want to make sure that based on the condition validator skip further chain validation.
You could do it in stages. Here I first check whether username is set, and only if that passes do I then do an expensive async validation.
req.checkBody('username')
.notEmpty().withMessage('Username is required.');
req.getValidationResult().then(result => {
if (!result.isEmpty()) {
return res.status(400).send(result.mapped());
}
req.checkBody('username')
.isUserNameAvailable().withMessage('Username is not available.');
req.getValidationResult().then(result => {
if (!result.isEmpty()) {
return res.status(400).send(result.mapped());
}
next();
});
})
Related
I'm using cognito authentication,
I create a middlware
const { email } = payload;
req.headers['user-email'] = email as string;
I want to write this kind of function
public async httpCheck(query: any, args: any, context: any,
resolveInfo: any) {
console.log('authhealth');
console.log("context "+ context.userEmail);
console.log("query : "+ query.userEmail);
(context.userEmail === query.userEmail ) ? console.log("authorized successfully") : console.log("authorization failed");
return 'OK';
}
This is my file structure, I want to write wrap resolver
From your example, it looks like you are wanting to reject the whole request if the email from the request header does not match an email being provided as an argument to a field in the GraphQL query.
So given the following query:
query MyQuery($userEmail:String!) {
userByEmail(email: $userEmail) {
id
email
familyName
givenName
}
}
If you want to check that the header email equals the email argument of userByEmail BEFORE Postgraphile executes the operation, you need to use a Postgraphile Server Plugin which adds a dynamic validation rule that implements the check:
import type { PostGraphilePlugin } from "postgraphile";
import type { ValidationRule } from "graphql";
import { GraphQLError } from "graphql";
import type { IncomingMessage } from "http";
import type { Plugin } from "graphile-build";
// Defines a graphile plugin that uses a field argument build hook to add
// metadata as an extension to the "email" argument of the "userByEmail" field
const AddEmailMatchPlugin: Plugin = (builder) => {
builder.hook(
"GraphQLObjectType:fields:field:args",
(args, build, context) => {
// access whatever data you need from the field context. The scope contains
// basically any information you might desire including the database metadata
// e.g table name, primary key.
const {
scope: { fieldName, isRootQuery },
} = context;
if (!isRootQuery && fieldName !== "userByEmail") {
return args;
}
if (args.email) {
return {
...args,
email: {
...args.email,
// add an extensions object to the email argument
// this will be accessible from the finalized GraphQLSchema object
extensions: {
// include any existing extension data
...args.email.extensions,
// this can be whatetever you want, but it's best to create
// an object using a consistent key for any
// GraphQL fields/types/args that you modify
myApp: {
matchToUserEmail: true,
},
},
},
};
}
return args;
}
);
};
// define the server plugin
const matchRequestorEmailWithEmailArgPlugin: PostGraphilePlugin = {
// this hook enables the addition of dynamic validation rules
// where we can access the underlying http request
"postgraphile:validationRules": (
rules,
context: { req: IncomingMessage; variables?: Record<string, unknown> }
) => {
const {
variables,
// get your custom user context/jwt/headers from the request object
// this example assumes you've done this in some upstream middleware
req: { reqUser },
} = context;
if (!reqUser) {
throw Error("No user found!");
}
const { email, role } = reqUser;
const vr: ValidationRule = (validationContext) => {
return {
Argument: {
// this fires when an argument node has been found in query AST
enter(node, key) {
if (typeof key === "number") {
// get the schema definition of the argument
const argDef = validationContext.getFieldDef()?.args[key];
if (argDef?.extensions?.myApp?.matchToUserEmail) {
// restrict check to a custom role
if (role === "standard") {
const nodeValueKind = node.value.kind;
let emailsMatch = false;
// basic case
if (nodeValueKind === "StringValue") {
if (node.value.value === email) {
emailsMatch = true;
}
}
// must account for the value being provided by a variable
else if (nodeValueKind === "Variable") {
const varName = node.value.name.value;
if (variables && variables[varName] === email) {
emailsMatch = true;
}
}
if (!emailsMatch) {
validationContext.reportError(
new GraphQLError(
`Field "${
validationContext.getFieldDef()?.name
}" argument "${
argDef.name
}" must match your user email.`,
node
)
);
}
}
}
}
},
},
};
};
return [...rules, vr];
},
// This hook appends the AddEmailMatchPlugin graphile plugin that
// this server plugin depends on for its custom extension.
"postgraphile:options": (options) => {
return {
...options,
appendPlugins: [...(options.appendPlugins || []), AddEmailMatchPlugin],
};
},
};
export default matchRequestorEmailWithEmailArgPlugin;
Then you need to register the server plugin in the Postgraphile middleware options:
const pluginHook = makePluginHook([MatchRequestorEmailWithEmailArgPlugin]);
const postGraphileMiddleware = postgraphile(databaseUrl, "my_schema", {
pluginHook,
// ...
});
If you just want to reject the userByEmail field in the query and don't care about rejecting before any resolution of any other parts of the request occur, you can use the makeWrapResolversPlugin to wrap the resolver and do the check there.
Hey guys
I wonder if I miss something here, iv'e trying to figure it out for a few hours and didn't came up with a solution.
I'm trying to use form validations using Vuelidate in vue, everything seems in place but after the custom alert I created, the form is still proceeding to the next stage.
I declared Vuelidate like this:
import useVuelidate from '#vuelidate/core'
import { required } from '#vuelidate/validators'
Inside Data() I did as follows:
data: () => ({
v$: useVuelidate(),
currentStage: 1,
traffic: {
unique: '',
unique1: '',
unique2: '',
unique3: '',
unique4: ''
},
}),
Declared validations outside of data(), like this:
validations() {
return {
traffic: {
unique1: { required },
unique2: { required },
unique3: { required },
unique4: { required },
unique5: { required }
},
}
},
Last thing is the computed, which I have function that is creating the input fields in stage 3 of the form:
computed: {
appendFields() {
this.v$.$validate()
if(!this.v$.$error){
if(this.jsonStatham.hasOwnProperty(this.traffic.unique1)){
this.integrationParams.push(...Object.keys(this.jsonStatham[this.traffic.unique1]))
}
} else {
alert("Error, Not all fields are filled in.")
}
}
},
So here is the problem, when appendFields() called, I do get this alert: alert("Error, Not all fields are filled in.")
But After I press "ok" in the alert, the form is still proceeding to the next stage.
What am I missing?
Edit:
This is the button who execute the "appendFields" method:
<button #click="buttonClicked(0); appendFields;">Next Stage</button>
And this is buttonClicked function:
buttonClicked(value) {
if(value === 0){
this.currentStage++;
return this.currentStage;
}
if(value === 1){
this.currentStage--;
return this.currentStage;
}
},
The click-handler updates currentStage without first validating the form. However, the validation occurs in appendFields, which is computed after buttonClicked(). The validation should be the first step, which can block the proceeding steps.
I would refactor it like this:
Make appendFields a component method, since it's not really a computed property (especially because it returns nothing).
Move the currentStage update into its own function for clarity.
Move the form validation from appendFields() to the button's click-handler.
In the click-handler, call the functions created in step 1 and 2 if the form is valid.
export default {
methods: {
// 1️⃣
appendFields() {
if (this.jsonStatham.hasOwnProperty(this.traffic.unique1)) {
this.integrationParams.push(...Object.keys(this.jsonStatham[this.traffic.unique1]))
}
},
// 2️⃣
updateStage(value) {
if (value === 0) {
this.currentStage++
} else if (value === 1) {
this.currentStage--
}
},
buttonClicked(value) {
// 3️⃣
this.v$.$validate()
if (!this.v$.$error) {
// 4️⃣
this.appendFields()
this.updateStage(value)
} else {
alert("Error, Not all fields are filled in.")
}
}
}
}
Also be aware that useValidate() is intended for the Composition API, so it should be called inside setup():
export default {
setup() {
return {
v$: useValidate()
}
}
}
I want to update v-model value by code and validate that field. So I am using vue.set method for updating value and then call $validator.validate.
my code is something like that.
Vue.set(model,property, value);
vm.$validator.validate(property).then(function (valid) {
if (!valid) {
vm.$validator.flag(property, {
touched: true,
dirty: true
});
}
});
my validation rules code is somethng like that:
Validator.extend("val_compulsory", {
getMessage(field, args) {
return args[0];
},
validate(value) {
return {
valid: !!value,
data: {
required: true
}
};
}
}, { computesRequired: true });
but in val_compulsory validator I always get previous value which is before vue.set. Is there any way to get latest value in vee-validator validation methods after vue.set?
Try this:
Vue.set(model,property, value);
vm.$nextTick(function() {
vm.$validator.validate(property).then(function (valid) {
if (!valid) {
vm.$validator.flag(property, {
touched: true,
dirty: true
});
}
});
});
I have a model "User" that has a Many-to-One relationship with a "Subject".
User.js
attributes: {
subject: { model: 'subject' },
}
Subject.js
attributes: {
name: { type: 'string', unique: true, required: true },
}
When I call the blueprint create function for a User "/user" and pass in the data:
{
"name":"Test",
"subject":{"name":"Do Not Allow"}
}
It creates the user and also creates the Subject. However I do not want to allow the subject to be created, I only want to be able to attach an existing one. For example I would like it to reject the subject being created using the above data but allow the subject to be attached by using the below data.
{
"name":"Test",
"subject":1
}
I tried adding a policy (shown below) but this only stops the subject from being created using the URL "/subject" and not the nested create shown above.
'SubjectController':{
'create':false
}
Edit
To help understand what is going on here this is the lifecycle process it is going through:
Before Validation of Subject
After Validation of Subject
Before Creating Subject
After Creating Subject
Before Validation of User
After Validation of User
Before Creating User
Before Validation of User
After Validation of User
After Creating User
As you can see it is validating and creating the subject before it even gets to validating or creating the user.
You want to avoid the creation of an associated object when calling the blueprint creation route.
Create a policy (I've named it checkSubjectAndHydrate) and add it into the policies.js file:
// checkSubjectAndHydrate.js
module.exports = function (req, res, next) {
// We can create a user without a subject
if (_.isUndefined(req.body.subject)) {
return next();
}
// Check that the subject exists
Subject
.findOne(req.body.subject)
.exec(function (err, subject) {
if (err) return next(err);
// The subject does not exist, send an error message
if (!subject) return res.forbidden('You are not allowed to do that');
// The subject does exist, replace the body param with its id
req.body.subject = subject.id;
return next();
});
};
// policies.js
module.exports.policies = {
UserController: {
create: 'checkSubjectAndHydrate',
update: 'checkSubjectAndHydrate',
}
};
You should be passing the subject id (e.g. 1) instead of an object (e.g. { name: 'Hello, World!' }) containing the name of the subject as it's not necessarily unique.
If it is unique, you should replace the object by its id inside a beforeValidate for example.
// User.js
module.exports = {
...
beforeValidate: function (users, callback) {
// users = [{
// "name":"Test",
// "subject":{"name":"Do Not Allow"}
// }]
async.each(users, function replaceSubject(user, next) {
var where = {};
if (_.isObject(user.subject) && _.isString(user.subject.name)) {
where.name = user.subject.name;
} else if(_.isInteger(user.subject)) {
where.id = user.subject;
} else {
return next();
}
// Check the existence of the subject
Subject
.findOne(where)
.exec(function (err, subject) {
if (err) return next(err);
// Create a user without a subject if it does not exist
user.subject = subject? subject.id : null;
next();
});
}, callback);
// users = [{
// "name":"Test",
// "subject":1
// }]
}
};
You can create custom type for subject, and add your logic inside model. I'm not 100% sure I understood the attach sometimes part but maybe this could help:
models/User.js
module.exports = {
schema: true,
attributes: {
name: {
type: 'string'
},
subject: {
type: 'json',
myValidation: true
}
},
types: {
myValidation: function(value) {
// add here any kind of logic...
// for example... reject if someone passed name key
return !value.name;
}
}
};
You can find more info here http://sailsjs.org/documentation/concepts/models-and-orm/validations at the bottom of the page.
If I totally missed the point... The second option would be to add beforeCreate and beforeUpdate lifecycle callback to your model like this:
models/User.js
module.exports = {
schema: true,
attributes: {
name: {
type: 'string'
},
subject: {
type: 'json'
}
},
beforeCreate: function (values, cb) {
// for example... reject creating of subject if anything else then value of 1
if (values.subject && values.subject !== 1) return cb('make error obj...');
cb();
},
beforeUpdate: function (values, cb) {
// here you can add any kind of logic to check existing user or current update values that are going to be updated
// and allow it or not
return cb();
}
};
By using this you can use one logic for creating and another one for updating... etc...
You can find more info here: http://sailsjs.org/documentation/concepts/models-and-orm/lifecycle-callbacks
EDIT
Realized you have trouble with relation, and in above examples I thought you are handling type json...
module.exports = {
schema: true,
attributes: {
name: {
type: 'string'
},
subject: {
model: 'subject'
}
},
beforeValidate: function (values, cb) {
// subject is not sent at all, so we just go to next lifecycle
if (!values.subject) return cb();
// before we update or create... we will check if subject by id exists...
Subject.findOne(values.subject).exec(function (err, subject) {
// subject is not existing, return an error
if (err || !subject) return cb(err || 'no subject');
//
// you can also remove subject key instead of sending error like this:
// delete values.subject;
//
// subject is existing... continue with update
cb();
});
}
};
I'm using babel with decorator stage:0 support in my flux fluxible js project, and I want to use an authenticated decorator for my service api modules to check for a valid user session.
Googling around, there seems to be several posts that explain different variations but couldn't find one definitive docs or instructionals.
Here's what I tried so far, I know that my parameters for the authenticated function are incorrect, and not sure if I need to implement a class for my module rather than just using the exports object.
The part that I couldn't find the docs for is how to implement the decorator itself - in this case something that takes the req parameter the decorated function will receive and checking it.
// how do I change this method so that it can be implemented as a decorator
function checkAuthenticated(req) {
if (!req.session || !req.session.username)
{
throw new Error('unauthenticated');
}
}
module.exports = {
#checkAuthenticated
read: function(req, resource, params, serviceConfig, callback) {
//#authenticated decorator should allow me to move this out of this here
//checkAuthenticated(req);
if (resource === 'product.search') {
var keyword = params.text;
if (!keyword || keyword.length === 0) {
return callback('empty param', null);
} else {
searchProducts(keyword, callback);
}
}
}
};
class Http{
#checkAuthenticated
read(req, resource, params, serviceConfig, callback) {
if (resource === 'product.search') {
var keyword = params.text;
if (!keyword || keyword.length === 0) {
return callback('empty param', null);
} else {
this.searchProducts(keyword, callback);
}
}
}
searchProducts(keyword, callback) {
callback(null, 'worked');
}
}
function checkAuthenticated(target, key, descriptor) {
return {
...descriptor,
value: function(){
console.log(arguments);
const req = arguments[0];
if (!req.session || !req.session.username) {
throw new Error('unauthenticated');
}
return descriptor.value.apply(this, arguments);
}
};
}
let h = new Http();
h.read(
{ session: { username: 'user' } },
'product.search',
{ text: 'my keywords' },
null,
function(err, result) {
if (err) return alert(err);
return alert(result);
}
);
See jsbin http://jsbin.com/yebito/edit?js,console,output