C
C#6h ago
Yarden

Controller uses Dto file to create a new user, does the Model it self needs to include restrictions?

Hello! I have a model of "User" which include User basic attributes, I also added restrictions like password length (max, min and errors). When the controller creates a new user, it uses a Dto file (which obviously has less attributes than the Model it self) and then mapping it into the model which stores it into the database. Does the Model needs the restrictions or the Dto is enough for restrictions? For example: (picture)
No description
47 Replies
kurumi
kurumi5h ago
It depends on your architecture Normally it would be really nice to control all you invariants very close to domain model itself (on very bottom layer of application). In this case you can guarantee that any upper layer would follow your restrictions. And then this restrictions also propagates to upper layers up to representation layers (aka WEB API in most cases) If your application is not very complex, just do the easiest way. If you have very complex logic and you need STRICTLY CONTROL that only valid models exists - then use what I said above and normally DTOs don't have any restrictions. They purpose to be data transfer objects so I think you mean not DTO's but any other architecture layer models
Yarden
YardenOP5h ago
I was following a video where he used Dto in order to create\read an object without the need of the irrelevant attributes like id, etc... He didn't put restrictions in the Dto layer but I did, because it seems like the Model it self doesn't control the restrictions when I use Dto, and then use the Swagger for the Controller methods. For example: When I put Dto to create a new User for my database and I don't put restriction like "Minimum password length = 8" and I put this restriction only in the Model it self, and then I go to the Swagger and use the "Register" method in the Controller, it doesn't stop me from creating an object with password of length less than 8. It seems like the restrictions don't exist. In the picutre it is the Controller method: I know there are better ways to create Controller (someone gave me 2 articles to read about it, I'll do it after I'm done)
No description
kurumi
kurumi5h ago
I see, but that means it is no longer DTO, bcs DTO only responsible for transfering data - not holding invariants
Angius
Angius5h ago
The model represents the database row, so it should have whatever restrictions you want to be on the database
Yarden
YardenOP5h ago
Werid, it didn't work for me, maybe I did something wrong. I'll update in 5 minutes Thank you very much for the help umtil this moment
kurumi
kurumi5h ago
There are dozen books and articles of domain driven development with clean architecture. You can take a look at that to deeply understand things
Yarden
YardenOP5h ago
The PasswordHash with restriction belongs to the Model The PasswordHash without the restrictions belongs to the Dto I've created a user with password of length 2 and yet it lets me
No description
No description
No description
Yarden
YardenOP5h ago
I'm in race against time, I'm doing this as a degree project, so I don't have much time to freely check many resources (eventhough I want honestly)
kurumi
kurumi5h ago
I am curious, why is your API have PasswordHash as parameter? And why it is query? It is not the way it should be then do the easiest solution for you. There is no need to be worry unless you will have a problems with it's support I also suggest you to move all /Register endpoint data inside a body - not a query string especially the password
Yarden
YardenOP5h ago
I watched a video where the instructor used normal "password" attribute but then chatGPT recomended to convert it into hash in order to make it more secured. I have no idea what this (query) is honestly, I'm just flowing with it :nervousowo:
kurumi
kurumi5h ago
Look
Angius
Angius5h ago
Storing as hash in the database — yes Sending hash from the client — why
kurumi
kurumi5h ago
You are creating an API for external usage. Hashing the password is a logic of storing a password in secure manier in DB. And there is no any point to the external user to know the hash. Hash only exist for your infrastructure inside ur app external client should "know" as less as possible I would say
Yarden
YardenOP5h ago
I'm confused But until this moment when I used the Swagger I totally put a normal password like "11" and then in the Database I saw it got converted into this weird long hash code. Am I wrong? I'm sorry, I'm kind of beginner so I have weird questions
Angius
Angius5h ago
When user registers, they send a plaintext password, the backend hashes it, and stores it in the database. When user logs in, they send a plaintext password and login/email, the backend finds the password hash by the login/email and checks if it checks out The user does not send or receive the hash at any point
Yarden
YardenOP5h ago
Yes I understand this logic, makes sense So wait, the problem is my Dto attribute name? That I named is PasswordHash instead of password?
Angius
Angius5h ago
That would be the source of confusion here, yes
Yarden
YardenOP5h ago
It used to be "Password" I changed it because I thought it was wrong 😂 my bad Is that all with the password hash problem? or am I missing more? And thank you very much
Angius
Angius5h ago
Far as I can tell, yeah Not sure if BCrypt.HashPassword() salts the password before hashing That could be another issue if it doesn't
Yarden
YardenOP5h ago
What do you mean by "salts"?
Angius
Angius5h ago
If one person has hunter12 for password, and another has hunter12 for password, that will provide the same hash That's why passwords are salted before hashing, for example into 12345-hunter12 and 67890-hunter12 Those will provide different hashes
Yarden
YardenOP5h ago
ohh cool I think that what happens, I can check
kurumi
kurumi5h ago
The flow you need is getting the data from a client:
public record RegisterRequestData(
string UserName,
string Password,
string FirstName
string LastName,
enum Gender
);
public record RegisterRequestData(
string UserName,
string Password,
string FirstName
string LastName,
enum Gender
);
then you need a function to create a hash:
public class PasswordHasher
{
public string HashPassword(string rawPassword)
{
// the logic to hash password
}

public bool CheckPasswordHash(string rawPassword, string passwordHash)
{
// the logic to check if hash of rawPassword == passwordHash
}
}
public class PasswordHasher
{
public string HashPassword(string rawPassword)
{
// the logic to hash password
}

public bool CheckPasswordHash(string rawPassword, string passwordHash)
{
// the logic to check if hash of rawPassword == passwordHash
}
}
after that create a database user representation:
public class UserData // this is database representation
{
public string UserName { get; set; }
public string PasswordHash { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public enum Gender { get; set; }
}
public class UserData // this is database representation
{
public string UserName { get; set; }
public string PasswordHash { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public enum Gender { get; set; }
}
and insert it to DB and before storing in DB, you should create a model of user:
public class UserModel
{
public string UserName { get; init; }
// public string Password { get; init; } <----- !!! there is no need for password here !!! you dont need this prop, you application doesnt care of user password
public string FirstName { get; init; }
public string LastName { get; init; }
public enum Gender { get; init; }

public UserModel(string userName, string firstName, string lastName, enum gender)
{
// if checks for invariants, e.x. userName is not empty
// assign values
}
}
public class UserModel
{
public string UserName { get; init; }
// public string Password { get; init; } <----- !!! there is no need for password here !!! you dont need this prop, you application doesnt care of user password
public string FirstName { get; init; }
public string LastName { get; init; }
public enum Gender { get; init; }

public UserModel(string userName, string firstName, string lastName, enum gender)
{
// if checks for invariants, e.x. userName is not empty
// assign values
}
}
it is very important to create UserData from UserModel - not from RegisterRequestData !!!!!!! the model can be only exist if it's valid and you can check the password length before creating an instance of UserModel 1. get the data from a user 2. validate data 3. create instance of model 4. do some extra logic with instance of model 5. create an instance of database model 6. attach it to database and if you have multiple layers, you can define your checks in each
Yarden
YardenOP5h ago
Omg thank you very much, I appreciate it ❤️ Why is that method important? "public bool CheckPasswordHash" I don't udnerstand its purpose What that means "if hash of rawPassword == passwordHash"? If the user puts a plaintext password like "11" and it converts it into hashType then why do we need to check the hashType password? what exactly it validates?
kurumi
kurumi4h ago
Hash function is one way function. That means having password 111 you can get it's hash like 6216f8a75fd5bb3d5f22b6f9958cdede3fc086c2 (sha1 btw). But by having the hash you can not get the password back. If you database was compromised, hackers would get only hashes and not passwords. If your users use same password for all websites hackers won't get theirs google accounts So in this case how can we validate if user inputs the password and it is his password? So we MUST need a function to validate if 111 == 6216f8a75fd5bb3d5f22b6f9958cdede3fc086c2 somehow Validate could be like 1. get the password hash from database for this specific user 2. generate a temp hash with same hash function from user input 3. check if both hashes are match each other In the realty it's a bit more complicated. And we use HMAC for tokens to give them to users and validate as well and another thing, don't use MD5 / SHA-1 or even SHA-256 for passwords. They are no longer secure. You can brute force them. I suggest to take a look at Argon2
Yesn't
Yesn't4h ago
not even SHA-256?
Yarden
YardenOP4h ago
1 things I'm confused with When your usera gives a password like "111" and you get something like "6216f8a75fd5bb3d5f22b6f9958cdede3fc086c2" And then a user wants to login with his current password, so he inserts "111", so what you want to do is: 1. get the password hash from database for this specific user -> (Which means we go to the account inside the database, gets the hash version from there) 2. generate a temp hash with same hash function from user input -> You put the given plaintext the guy gave which was "111" again and generates it into Hash, The hash function will generate the same hash password again? like "6216f8a75fd5bb3d5f22b6f9958cdede3fc086c2"? So the passwords are identical and the user can login? But if the hashes are identical, then they will be generated the same also in other websites. 111 will be equal to ""6216f8a75fd5bb3d5f22b6f9958cdede3fc086c2"" no?
Yesn't
Yesn't4h ago
i dont get it hashes are designed for "same input, same output", so yes, if someone input "111" and get "xyz" then later they "111" they'll get "xyz"
Yarden
YardenOP4h ago
always right?
Yesn't
Yesn't4h ago
always, it's an unchanging mathematic algorithm you cant turn hash back into password (you technically can but no one practically done that)
Yarden
YardenOP4h ago
But then 2 users who give the same plaintext password will have the same hash-code no?
kurumi
kurumi4h ago
yeap, sha-256 is very easy to compute. Argon2 makes it slower which means it brute forces much longer. It has time cost, memory cost and parallelism
Yesn't
Yesn't4h ago
same input, same output
Yarden
YardenOP4h ago
perfect
Yesn't
Yesn't4h ago
next time use RSA-4096 to encrypt ur password instead of hash comparison :kekw:
Yarden
YardenOP4h ago
@kurumi Thank you so much for the long explanations, you are amazing @ZZZZZZZZZZZZZZZZZZZZZZZZZ Thank you for the long help @EntityRef<Ashy> thank you for the last explanation, helped much I think I got it
Yesn't
Yesn't4h ago
you got it good luck
kurumi
kurumi4h ago
$close makes sense?
MODiX
MODiX4h ago
If you have no further questions, please use /close to mark the forum thread as answered
Yarden
YardenOP4h ago
thank you very much
Yesn't
Yesn't4h ago
makes sense
Yarden
YardenOP4h ago
If I type /close the conversation won't disappear right?
Yesn't
Yesn't4h ago
idk :kekw: my first time helping
kurumi
kurumi4h ago
it will just marked as resolved
Yarden
YardenOP4h ago
perfect
kurumi
kurumi4h ago
you can extend it after, edit or ofc view
Yarden
YardenOP4h ago
thanks again ❤️

Did you find this page helpful?