Change User default validation in Loopback - validation

I'm developping a Loopback application extending base User model to UserCode model where each user is identified by an email plus a code fields.
So that a user can register with the same email twice but with different code.
I've seen that in node_modules/loopback/common/models/user.js at line 691 there is:
UserModel.validatesUniquenessOf('email', {message: 'Email already exists'});
I want to delete this restriction/validation but without change loopback code, of course.
How can I do it?
Maybe in the boot script I can loop through all validation and delete this one?

Figured it out
In this case you need to remove the default validations set by the User model
common/models/userCode.js
module.exports = function(UserCode){
//Add this line and it will start receiving multiple email.
delete UserCode.validations.email;
}
Also you can play with the required:true|false property to make any default defined property required or not.
common/models/userCode.json
{
"name": "UserCode",
"base": "User",
"idInjection": true,
"properties": {
"password": {
"type": "string",
"required": true
},
....
....
}

The following code the accepted answer will remove ALL the email validations:
module.exports = function(UserCode){
//Add this line and it will start receiving multiple email.
delete UserCode.validations.email;
}
Instead be selective and do something like this:
module.exports = function(UserCode){
// remove ONLY email uniqueness validation
UserCode.validations.email = UserCode.validations.email.reduce((all, one) => {
if (one.validation !== 'uniqueness') {
all.push(one);
}
return all;
}, []);
}

Related

How to add custom route for user

I have created a books content type containg books. Each book in the collection belongs to a user (user content type provided by Strapi).
I want to return list of books owned by authenticated user at /users/me/books endpoint. Where can I add this route and handler as there is /api/books directory containing books related route, controllers, etc. but not /api/users directory.
You can extend or override using the extensions system.
extensions/users-permissions/controllers
Just add the controller you want to extend or override as a .js file like so:
So to override the me endpoint under User.js you only need to define the method again:
'use strict';
module.exports = {
//Override me
async me(ctx) {
//do your thing
}
};
To extend, not override, means to add another endpoint, therefor you need to define it, add a route and set permissions for it. The routes.js files should be created at:
extensions/users-permissions/config/routes.json
Like so:
{
"routes": [
{
"method": "GET",
"path": "/users/me/books",
"handler": "User.getUserBooks",
"config": {
"policies": [],
"prefix": "",
"description": "description",
"tag": {
"plugin": "users-permissions",
"name": "User",
"actionType": "find"
}
}
}
}
The controller this time (same location as in beginning):
module.exports = {
async getUserBooks(ctx) {
//add logic
}
}
OP correctly added:
After adding custom route and controller, one has to go to Admin Panel(log in as admin)>Roles and Permission> Users-Permission. There you can find the newly added route and have to enable it by checking it.
The originals(if you need examples) are located at:
/node_modules/strapi-plugin-users-permissions/config/routes.json
/node_modules/strapi-plugin-users-permissions/controllers/User.js
I don't think you should extend the User controller as it isn't logically correct. You are trying to GET books - you should extend the book api in the same way.
From what I can tell a ContentType doesn't contain information about its creator(you're welcome to educate me if it's not true).
So to tackle that you can add to your ContentType "books" a relation to User.
Then I think you should extend the books api with a endpoint that returns books "belonging" to that user using the ctx received.
Also - check this question out
Comment if you need more info.

Get complete GraphQL response using POST without specify field name in request [duplicate]

Assume you have a GraphQL type and it includes many fields.
How to query all the fields without writing down a long query that includes the names of all the fields?
For example, If I have these fields :
public function fields()
{
return [
'id' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The id of the user'
],
'username' => [
'type' => Type::string(),
'description' => 'The email of user'
],
'count' => [
'type' => Type::int(),
'description' => 'login count for the user'
]
];
}
To query all the fields usually the query is something like this:
FetchUsers{users(id:"2"){id,username,count}}
But I want a way to have the same results without writing all the fields, something like this:
FetchUsers{users(id:"2"){*}}
//or
FetchUsers{users(id:"2")}
Is there a way to do this in GraphQL ??
I'm using Folkloreatelier/laravel-graphql library.
Unfortunately what you'd like to do is not possible. GraphQL requires you to be explicit about specifying which fields you would like returned from your query.
Yes, you can do this using introspection. Make a GraphQL query like (for type UserType)
{
__type(name:"UserType") {
fields {
name
description
}
}
}
and you'll get a response like (actual field names will depend on your actual schema/type definition)
{
"data": {
"__type": {
"fields": [
{
"name": "id",
"description": ""
},
{
"name": "username",
"description": "Required. 150 characters or fewer. Letters, digits, and #/./+/-/_ only."
},
{
"name": "firstName",
"description": ""
},
{
"name": "lastName",
"description": ""
},
{
"name": "email",
"description": ""
},
( etc. etc. ...)
]
}
}
}
You can then read this list of fields in your client and dynamically build a second GraphQL query to get the values of these fields.
This relies on you knowing the name of the type that you want to get the fields for -- if you don't know the type, you could get all the types and fields together using introspection like
{
__schema {
types {
name
fields {
name
description
}
}
}
}
NOTE: This is the over-the-wire GraphQL data -- you're on your own to figure out how to read and write with your actual client. Your GraphQL javascript library may already employ introspection in some capacity. For example, the apollo codegen command uses introspection to generate types.
2022 Update
Since this answer was originally written, it is now a recommended security practice to TURN OFF introspection in production. Reference: Why you should disable GraphQL introspection in production.
For an environment where introspection is off in production, you could use it in development as a way to assist in creating a static query that was used in production; you wouldn't actually be able to create a query dynamically in production.
I guess the only way to do this is by utilizing reusable fragments:
fragment UserFragment on Users {
id
username
count
}
FetchUsers {
users(id: "2") {
...UserFragment
}
}
I faced this same issue when I needed to load location data that I had serialized into the database from the google places API. Generally I would want the whole thing so it works with maps but I didn't want to have to specify all of the fields every time.
I was working in Ruby so I can't give you the PHP implementation but the principle should be the same.
I defined a custom scalar type called JSON which just returns a literal JSON object.
The ruby implementation was like so (using graphql-ruby)
module Graph
module Types
JsonType = GraphQL::ScalarType.define do
name "JSON"
coerce_input -> (x) { x }
coerce_result -> (x) { x }
end
end
end
Then I used it for our objects like so
field :location, Types::JsonType
I would use this very sparingly though, using it only where you know you always need the whole JSON object (as I did in my case). Otherwise it is defeating the object of GraphQL more generally speaking.
GraphQL query format was designed in order to allow:
Both query and result shape be exactly the same.
The server knows exactly the requested fields, thus the client downloads only essential data.
However, according to GraphQL documentation, you may create fragments in order to make selection sets more reusable:
# Only most used selection properties
fragment UserDetails on User {
id,
username
}
Then you could query all user details by:
FetchUsers {
users() {
...UserDetails
}
}
You can also add additional fields alongside your fragment:
FetchUserById($id: ID!) {
users(id: $id) {
...UserDetails
count
}
}
Package graphql-type-json supports custom-scalars type JSON.
Use it can show all the field of your json objects.
Here is the link of the example in ApolloGraphql Server.
https://www.apollographql.com/docs/apollo-server/schema/scalars-enums/#custom-scalars

Do a full query simply in GraphQL [duplicate]

Assume you have a GraphQL type and it includes many fields.
How to query all the fields without writing down a long query that includes the names of all the fields?
For example, If I have these fields :
public function fields()
{
return [
'id' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The id of the user'
],
'username' => [
'type' => Type::string(),
'description' => 'The email of user'
],
'count' => [
'type' => Type::int(),
'description' => 'login count for the user'
]
];
}
To query all the fields usually the query is something like this:
FetchUsers{users(id:"2"){id,username,count}}
But I want a way to have the same results without writing all the fields, something like this:
FetchUsers{users(id:"2"){*}}
//or
FetchUsers{users(id:"2")}
Is there a way to do this in GraphQL ??
I'm using Folkloreatelier/laravel-graphql library.
Unfortunately what you'd like to do is not possible. GraphQL requires you to be explicit about specifying which fields you would like returned from your query.
Yes, you can do this using introspection. Make a GraphQL query like (for type UserType)
{
__type(name:"UserType") {
fields {
name
description
}
}
}
and you'll get a response like (actual field names will depend on your actual schema/type definition)
{
"data": {
"__type": {
"fields": [
{
"name": "id",
"description": ""
},
{
"name": "username",
"description": "Required. 150 characters or fewer. Letters, digits, and #/./+/-/_ only."
},
{
"name": "firstName",
"description": ""
},
{
"name": "lastName",
"description": ""
},
{
"name": "email",
"description": ""
},
( etc. etc. ...)
]
}
}
}
You can then read this list of fields in your client and dynamically build a second GraphQL query to get the values of these fields.
This relies on you knowing the name of the type that you want to get the fields for -- if you don't know the type, you could get all the types and fields together using introspection like
{
__schema {
types {
name
fields {
name
description
}
}
}
}
NOTE: This is the over-the-wire GraphQL data -- you're on your own to figure out how to read and write with your actual client. Your GraphQL javascript library may already employ introspection in some capacity. For example, the apollo codegen command uses introspection to generate types.
2022 Update
Since this answer was originally written, it is now a recommended security practice to TURN OFF introspection in production. Reference: Why you should disable GraphQL introspection in production.
For an environment where introspection is off in production, you could use it in development as a way to assist in creating a static query that was used in production; you wouldn't actually be able to create a query dynamically in production.
I guess the only way to do this is by utilizing reusable fragments:
fragment UserFragment on Users {
id
username
count
}
FetchUsers {
users(id: "2") {
...UserFragment
}
}
I faced this same issue when I needed to load location data that I had serialized into the database from the google places API. Generally I would want the whole thing so it works with maps but I didn't want to have to specify all of the fields every time.
I was working in Ruby so I can't give you the PHP implementation but the principle should be the same.
I defined a custom scalar type called JSON which just returns a literal JSON object.
The ruby implementation was like so (using graphql-ruby)
module Graph
module Types
JsonType = GraphQL::ScalarType.define do
name "JSON"
coerce_input -> (x) { x }
coerce_result -> (x) { x }
end
end
end
Then I used it for our objects like so
field :location, Types::JsonType
I would use this very sparingly though, using it only where you know you always need the whole JSON object (as I did in my case). Otherwise it is defeating the object of GraphQL more generally speaking.
GraphQL query format was designed in order to allow:
Both query and result shape be exactly the same.
The server knows exactly the requested fields, thus the client downloads only essential data.
However, according to GraphQL documentation, you may create fragments in order to make selection sets more reusable:
# Only most used selection properties
fragment UserDetails on User {
id,
username
}
Then you could query all user details by:
FetchUsers {
users() {
...UserDetails
}
}
You can also add additional fields alongside your fragment:
FetchUserById($id: ID!) {
users(id: $id) {
...UserDetails
count
}
}
Package graphql-type-json supports custom-scalars type JSON.
Use it can show all the field of your json objects.
Here is the link of the example in ApolloGraphql Server.
https://www.apollographql.com/docs/apollo-server/schema/scalars-enums/#custom-scalars

Adding a new text field pGina

I need to have a third text field besides the Username and Password fields commonly provided by pGina in windows logon UI. I'll be using this field to receive a password to be checked against a one-time password service running in the background.
How can I add the new field in the pGina logon UI and pass its value to a services running in the background?
Any help is appreciated.
I finally managed to to this.
As pointed out by #Alexander, I edited the TileUiLogon.h and TileUiTypes.h and followed the pattern to add a third field to the logon screen.
Then, I edited Credential::Initialize and added a new line in the "for" loop, following the same pattern for the "password" field (I'm not sure exactly what happens there, but since we're complying with the existing pattern, we don't care as long as the content of the new field is collected by the code similar to the other fields).
Since I didn't want to go through changing all the function signatures and mess with the code, I simply edited the Credential::ProcessLoginAttempt function and concatenated the content of the new field with that of the password field and embedded a custom delimiter to allow me separate the two strings in the following steps. After hitting the submit button, the fields data, prior to the real serialization, are initially sent to a pipe on the other end of which the pGina service is listening (pGinaTransactions.cpp). This service sends the login information to its plugins. I then edited the "Sample" plugin already provided and separated the two concatenated strings, immediately filling the password attribute of the object with the real password provided by the user, since these data will be sent back to the credential provider through pipe for further processing. If the plugin returns success, the password is then used for real serialization and logon attempt.
I have probably missed a few details, which you are very welcome to ask in the comments.
I think you must modify TileUiLogon.h file:
namespace pGina
{
namespace CredProv
{
// Fields for unlock and logon:
typedef enum LOGON_UI_FIELD_ID
{
LUIFI_TILEIMAGE = 0,
LUIFI_MOTD = 1,
LUIFI_USERNAME = 2,
LUIFI_PASSWORD = 3,
LUIFI_OTP = 4,
LUIFI_SUBMIT = 5,
LUIFI_STATUS = 6,
LUIFI_NUM_FIELDS = 7,
};
static const UI_FIELDS s_logonFields =
{
LUIFI_NUM_FIELDS, // Number of fields total
LUIFI_PASSWORD, // Field index which submit button should be adjacent to
LUIFI_USERNAME, // Username field index value
LUIFI_PASSWORD, // Password field index value
LUIFI_STATUS, // Status field
{
// when to display, style, field id, type, name data source value callback
{ { CPFS_DISPLAY_IN_BOTH, CPFIS_NONE }, { LUIFI_TILEIMAGE, CPFT_TILE_IMAGE, L"Image" }, SOURCE_NONE, NULL, NULL },
{ { CPFS_DISPLAY_IN_BOTH, CPFIS_NONE }, { LUIFI_MOTD, CPFT_SMALL_TEXT, L"MOTD" }, SOURCE_DYNAMIC, L"pGina", NULL },
{ { CPFS_DISPLAY_IN_SELECTED_TILE, CPFIS_FOCUSED }, { LUIFI_USERNAME, CPFT_EDIT_TEXT, L"Username" }, SOURCE_NONE, NULL, NULL },
{ { CPFS_DISPLAY_IN_SELECTED_TILE, CPFIS_NONE }, { LUIFI_PASSWORD, CPFT_PASSWORD_TEXT, L"Password" }, SOURCE_NONE, NULL, NULL },
{ { CPFS_DISPLAY_IN_SELECTED_TILE, CPFIS_NONE }, { LUIFI_OTP, CPFT_PASSWORD_TEXT, L"OTP" }, SOURCE_NONE, NULL, NULL },
{ { CPFS_DISPLAY_IN_SELECTED_TILE, CPFIS_NONE }, { LUIFI_SUBMIT, CPFT_SUBMIT_BUTTON, L"Submit" }, SOURCE_NONE, NULL, NULL },
{ { CPFS_DISPLAY_IN_BOTH, CPFIS_NONE }, { LUIFI_STATUS, CPFT_SMALL_TEXT, L"Status" }, SOURCE_STATUS, L"Status", NULL },
}
};
}
}
and other related files like pGinaTransactions.h and so on to handle new field. ;-)
As far as i know (if you're on Vista or above), you gonna have to make your own Credential Provider and register it.
For the interaction with the service, i'd say it depends wether it's running in local or on a distant server. Anyway, that's probably the easy part of the work.
UPDATE : I don't know pGina like AT ALL. But you should look at gina.rc (line 93) under DIALOGS. Seems to be an interesting place to begin.
Try to add a custom EDITEXT (by the way, lots of IDE most likely have a visualizor for those resources. I know that Visual Studio is one of them as i already experienced it.)
Visualizor and resource.h --> /!\ This is a screenshot of what it looks like and the resource.h.
//Third TEXTEDIT I just added
EDITTEXT IDC_CUSTOM_PASSWORD_TXT, 146, 88, 183, 12, ES_PASSWORD | ES_AUTOHSCROLL

Loopback custom password validation

very simple question: if I try to validate a password in a User model it seems I can only validate the already encrypted password?
So for example if I use
Customer.validatesLengthOf('password', { min: 8, message: 'Too short' })
Then the encrypted password is checked (which is always longer than 8 characters), so no good... If I try to use a custom validation, how can I get access to the original password (the original req.body.password basically)?
EDIT (August 20, 2019): I am unsure if this is still an issue in the latest loopback releases.
In fact, this is a known problem in loopback. The tacitly approved solution is to override the <UserModel>.validatePassword() method with your own. YMMV.
akapaul commented on Jan 10, 2017 •
I've found another way to do this. In common model User there is a
method called validatePassword. If we extend our UserModel from User,
we can redefine this method in JS, like following:
var g = require('loopback/lib/globalize');
module.exports = function(UserModel) {
UserModel.validatePassword = function(plain) {
var err,
passwordProperties = UserModel.definition.properties.password;
if (plain.length > passwordProperties.max) {
err = new Error (g.f('Password too long: %s (maximum %d symbols)', plain, passwordProperties.max));
err.code = 'PASSWORD_TOO_LONG';
} else if (plain.length < passwordProperties.min) {
err = new Error(g.f('Password too short: %s (minimum %d symbols)', plain, passwordProperties.min));
err.code = 'PASSWORD_TOO_SHORT';
} else if(!(new RegExp(passwordProperties.pattern, 'g').test(plain))) {
err = new Error(g.f('Invalid password: %s (symbols and numbers are allowed)', plain));
err.code = 'INVALID_PASSWORD';
} else {
return true;
}
err.statusCode = 422;
throw err;
};
};
This works for me. I don't think that g (globalize) object is required
here, but I added this, just in case. Also, I've added my validator
options in JSON definition of UserModel, because of Loopback docs
For using the above code, one would put their validation rules in the model's .json definition like so (see max, min, and pattern under properties.password):
{
"name": "UserModel",
"base": "User",
...
"properties": {
...
"password": {
"type": "string",
"required": true,
...
"max": 50,
"min": 8,
"pattern": "(?=.*[A-Z])(?=.*[!##$&*])(?=.*[0-9])(?=.*[a-z])^.*$"
},
...
},
...
}
ok, no answer so what I'm doing is using a remote hook to get access to the original plain password and that'll do for now.
var plainPwd
Customer.beforeRemote( 'create', function (ctx, inst, next) {
plainPwd = ctx.req.body.password
next()
})
Then I can use it in a custom validation:
Customer.validate( 'password', function (err, res) {
const pattern = new RegExp(/some-regex/)
if (plainPwd && ! pattern.test( plainPwd )) err()
}, { message: 'Invalid format' })
Ok I guess the above answer is quite novel and obviously is accepted, but If you want a real easy solution with just some basic validations done and not much code then loopback-mixin-complexity is the solution for you.
If you don't want to create another dependency then you can go ahead with a custom mixin, that you can add into your user model or any other model where you need some kind of validation and it would do the validation for you.
Here's a sample code for how to create such mixin
module.exports = function(Model, options) {
'use strict';
Model.observe('before save', function event(ctx, next) { //Observe any insert/update event on Model
if (ctx.instance) {
if(!yourValidatorFn(ctx.instance.password) )
next('password not valid');
else
next();
}
else {
if(!yourValidatorFn(ctx.data.password) )
next('password not valid');
else
next();
}
});
};

Resources