Symfony Password encryption in an event listener - symfony-2.1

I am using Symfony 2.1 and the built in security features.
I have set up an entity to store my users:
IGA\UserBundle\Entity\SfGuardUser:
type: entity
table: sf_guard_user
id:
id:
type: string
generator: { strategy: UUID }
fields:
username:
type: string
length: 255
unique: true
algorithm:
type: string
length: 255
nullable: true
salt:
type: string
length: 255
nullable: true
password:
type: string
length: 255
created_at:
type: datetime
last_login:
type: datetime
nullable: true
is_active:
type: boolean
default: 1
is_super_admin:
type: boolean
default: 0
lifecycleCallbacks:
prePersist: [ setCreatedAtValue ]
You might recognise the table structure from the old sfGuard plugin in Symfony 1.x. I have legacy databases, so I would like to keep the same table structure going forward.
I know I can encrypt passwords in the controller, but I want the passwords to be encrypted in a prePersist and preUpdate doctrine event so I can limit the encryption logic to one single function.
My event fires just fine, but I need to get the password encryption service from within the doctrine listener. This is my attempt so far:
<?php
namespace IGA\UserBundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use IGA\UserBundle\Entity\SfGuardUser;
class PasswordEncryptor implements ContainerAwareInterface
{
/**
* #var ContainerInterface
*/
private $container;
/**
* {#inheritDoc}
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
public function preUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$entityManager = $args->getEntityManager();
if ( ($entity instanceof SfGuardUser) && ($entity->needs_encoding) ) {
$factory = $this->container->get('security.encoder_factory');
$encoder = $factory->getEncoder($entity);
$entity->setSalt(microtime());
$encodedPassword = $encoder->encodePassword($password, $entity->getSalt());
$entity->setPassword($password);
}
}
public function prePersist(LifecycleEventArgs $args)
{
$this->preUpdate($args);
}
}
Can anyone help me out with this? Is this a good way of doing things?
It seems the best way to me, because no matter where the entity is updated and stored, the password is encrypted.
If I can only get access to that service!! HELP!!
This will also enable me to use the Sonata Admin Bundle, or any other similar admin generator to manage my users with no additional hassle.

Related

Why I'm getting Validation failed (numeric string is expected)

I have this code
#Get()
#ApiQuery({
name: "code",
type: String,
required: false,
})
#ApiQuery({
name: "id",
type: Number,
required: false,
})
async read(
#Query("entity") entity: string,
#Query("code") code: string,
#Query("id", ParseIntPipe) id: number
): Promise<Q> {
return this.service.readAsync({ where: { codigo: code, id: id } });
}
Why I'm getting Validation failed (numeric string is expected) when I request to http://localhost:3000/api/v1/endpoint?entity=a&code=b
I know is related with id param, but I don't know how to solve this.
I want to be able to use code or id params according my needs.
If I request to http://localhost:3000/api/v1/endpoint?entity=a&code=b&id=1 or http://localhost:3000/api/v1/endpoint?entity=a&id=1 all is fine.
here #Query("id", ParseIntPipe) id: number you're saying that the query parameter id is required and must be an integer.
Thus, if you do GET /endpoint?entity=a&code=b, it will reply with bad request as there's no id parameter.
You can use the DefaultValuePipe pipe if id should be optional and will have a fallback value.
If you don't want any default value, then you'll need to write your own pipe (that could extends ParseIntPipe). Or you could use the builtin one ValidationPipe with class-validator decorators.
ParserIntPipe doesn't work on optional parameters, from its source code, you can see
async transform(value: string, metadata: ArgumentMetadata): Promise<number> {
if (!this.isNumeric(value)) {
throw this.exceptionFactory(
'Validation failed (numeric string is expected)',
);
}
return parseInt(value, 10);
}
/**
* #returns `true` if `value` is a valid integer number
*/
protected isNumeric(value: string): boolean {
return (
['string', 'number'].includes(typeof value) &&
/^-?\d+$/.test(value) &&
isFinite(value as any)
);
}
As per Micael Levi answer, you either provide a default value using DefaultValuePipe in case it was missing, or you build your own custom pipe that pass parameter undefined value

how to convert JSON to XML with Nodejs?

i just know that the object Json can be an XMl file by the js2xml library,
so that's why I'm trying to convert the following json to XML,
How can I achieve this in NodeJS?
i can't find an answer or a documentation that can help me?
here is the model JSON
const UserSchema = new mongoose.Schema({
email: {
type: String,
required: [true, "Please provide email address"],
unique: true,
match: [
/^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]
{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
"Please provide a valid email",
],
},
password: {
type: String,
required: [true, "Please add a password"],
minlength: 6,
select: false,
},
const User = mongoose.model("User", UserSchema);
module.exports = User;
i used this exemple that didn't work for me
function groupChildren(obj) {
for(prop in obj) {
if (typeof obj[prop] === 'object') {
groupChildren(obj[prop]);
} else {
obj['$'] = obj['$'] || {};
obj['$'][prop] = obj[prop];
delete obj[prop];
}
}
return obj;
}
const xml2js = require('xml2js');
const obj = {
Level1: {
attribute: 'value',
Level2: {
attribute1: '05/29/2020',
attribute2: '10',
attribute3: 'Pizza'
}
}
};
const builder = new xml2js.Builder();
const xml = builder.buildObject(groupChildren(obj));
console.log(xml);
When converting JSON to XML, one has to ask: what XML do you want to convert it to? Does it have to be a specific XML format, or will any old XML do?
If any old XML will do, then you can usually find some library to do the job, such as js2xml or js2xmlparser. The problem with these libraries is that they usually offer very little control over how the XML is generated, especially for example if there are JSON objects with keys that are not valid XML names (which doesn't apply in your case).
If you want a specific XML format then I would recommend using XSLT 3.0, which is available on node.js in the form of Saxon-JS. [Disclaimer: my company's product]. If you are interested in pursuing this approach, then tell us what you want the output to look like, and we can help you create it.
There are many different packages for XML serialization.
Most of them enforce a specific XML and JSON mapping convention.
Others require you to build the XML document in code.
Finally, there are solutions that do this with decorators. Those give you freedom in defining the structure without having to build the document entirely in code.
As an example: the xml decorators package.
It means that you define the XML mapping using a class. Next, you define decorators on top of each field, to define how it should be mapped to XML.
import { XMLAttribute, xml } from 'xml-decorators';
const NS = 'ns';
export class User {
#XMLAttribute({namespace: NS})
private email:string;
#XMLAttribute({namespace: NS})
private password: string;
constructor(email: string, password: string) {
this.email = email;
this.password = password;
}
}
And finally, to actually serialize
const user = new User('foo#bar.com', 'secret');
const xml = xml.serialize(user);
Conceptually, this is certainly a robust solution, since it strongly resembles how java xml binding (JAXB) and C# xml serialization work.

Node.JS: How do you validate QueryParams in routing-controllers?

lets say you have an interface like this:
import { Get, QueryParam } from 'routing-controllers';
// ...
#Get('/students')
async getStudents(
#QueryParam('count') count?: number,
): Promise<void> {
console.log(count);
}
How do you ensure count is an int and not a float, for example? Something like this is not valid:
#IsInt() #QueryParam('count') count?: number,
IsInt can only be used on a class property, eg for a body model, not for a single parameter value. But according to. this https://github.com/typestack/routing-controllers#auto-validating-action-params it is possible:
This technique works not only with #Body but also with #Param,
#QueryParam, #BodyParam and other decorators.
I had missed this in the docs: https://github.com/typestack/routing-controllers#inject-query-parameters By injecting all of the QueryParams instead of individual QueryParam, you can validate them as a class model:
enum Roles {
Admin = "admin",
User = "user",
Guest = "guest",
}
class GetUsersQuery {
#IsPositive()
limit: number;
#IsAlpha()
city: string;
#IsEnum(Roles)
role: Roles;
#IsBoolean()
isActive: boolean;
}
#Get("/users")
getUsers(#QueryParams() query: GetUsersQuery) {
// here you can access query.role, query.limit
// and others valid query parameters
}
Also, make sure you don't use barrel-imports to import Enums, or open-api-generator will produce an error that the enum is undefined and not and object; eg avoid this: import { Roles } from '../..'

Typegoose props not saving

I'm having a problem using typegoose,
I have class like:
class UserType1 extends Typegoose
implements User{
#prop({required: true, unique: true})
_username: string | undefined;
#prop({required: true})
_password: string | undefined;
constructor(username:string, password: string){
this._username = username;
this._password = password;
}
async saveToDB(): Promis<void>{ // This method is refrenced inside User Interface
const model: ModelType<UserType1> = getModelForClass(UserType1);
await model.create(this);
}
<Other methods inside class>
}
Then I use the above code sequence like this:
const u: User = new UserType1("username", "password");
u.saveToDB();
then, a new record is saved inside UserType1 collection,
but it's empty. non of the _username, _password or other props are saved inside the record. Inside of it there's only _id and a variable called "__v" (which I don't know where it's come from.
from what i got as an answer in the comments about not using an unmaintained version, i would recommend to write your example in this way:
class UserType1 implements User {
#prop({ required: true, unique: true })
public _username!: string;
#prop({ required: true })
public _password!: string;
static async default(this: ReturnModelType<typeof UserType1>, username: string, password: string): Promise<void> {
await this.create({ _username: username, _password: password });
}
// <Other methods inside class>
}
const UserModel = getModelForClass(UserType1);
// somewhere in an async code block
await UserModel.default("user1", "somePassword");
Okay, So I left my code and started a new clean one, and It was working perfectly,
so I started investigating the difference between codes until I found out this small problem with importing packages, and it was unrelated to my code:
Instead of doing
import {prop} from "#typegoose/typegoose"
I was doing:
import {prop} from "typegoose"
Since it didn't give me any warning a worked without errors, I never realized this was the problem.
Hope this he

Sequelize is returning integer as string

I using nodejs v4 with sequelize, and I have a model like this:
var Device = sequelize.define('Device', {
id: {
type: DataTypes.BIGINT,
primaryKey: true,
autoIncrement: true
},
tenantId: {
type: DataTypes.BIGINT,
allowNull: false
},
token: {
type: DataTypes.STRING,
allowNull: false
}
}, {
tableName: 'devices'
});
When I select a device by id the type of id is a string, exemple:
Device.findById(9).then( function(result) {
console.log(result.toJSON().id + 10);
});
The output will be 910, rather than 19, so I look at json and a saw this:
{
id: "9"
tenantId: "123"
token: "adsadsdsa"
}
The id in found device is a string, but I defined it as a number...
Doesn't it should be { "id": 9 } ?
How can I select a device with the types that I defined previously?
BIGINT maximum value is 2^63-1, javascript can safely represent up to 2^53. To be on the safe side libraries return those numbers as strings.
If you want to have numbers instead of strings, you can use this library https://github.com/mirek/node-pg-safe-numbers which deals with this issue.
I found a fix to this problem on sequelize repo.
https://github.com/sequelize/sequelize/issues/4523
The pg module used for sequelize returns bigint as string because bigints are not guaranteed to fit into js numbers. So I change my model to use integer (DataTypes.INTEGER)
IMO, a smoother solution is to force parsing of int to int, instead of strings. Checkout this issue and comments.
Trying to put this line before your logic code worked for me, this forces parsing of int8 to int, instead of strings:
require("pg").defaults.parseInt8 = true;
...
try {
const list = await Posts.findAll();
console.log(JSON.stringify(list));
} catch (e) {
console.log(e.message);
}
By default, sequelize try format your results. You can set raw :true for get raw data

Resources