For me it doesn't make sense that an attribute is considered safe if it simply has a rule. For instance sometimes I define rules and do not set a specific scenario for them, simply because if the field is empty the validator won't be used anyways (so I do not need to).
What I was proposing in the first place is for attributes to be considered safe if they are set to be required
@Jonah: could you explain more why it doesn’t make sense that an attribute is considered safe if there is a rule applicable to it for the given scenario?
Here's my argument: given a scenario, if an attribute has a rule to be applied, that means we know it will be validated before being saved to database. Unsafe attributes are usually PK, FK, security-related attributes.
PK: we seldom list it in validation rules unless PK is something to be entered by user. In this case, having a rule does mean it is safe to be massively assigned.
FK: some should be set by user input (e.g. post category), some set in the program (e.g. post author). For the former, we should have a rule to ensure the category is set correctly. For the latter, no rule is needed because it doesn't come from user input. In both cases, our initial rule still works.
security-related attributes: the analysis is similar to FK.
As you can see, in all these cases, we don’t really care if the rule is required or not.
I think the idea is simple and convinient - but would make it easier to accidently create a security hole.
That would mean that you can't set rules for attributes that you deem unsafe.
Which means that you have to trust your co-workers and those 'authorized' people to put correct data in, because it can't be validated (with rules).
For an example: Let's say an attributed called 'securityLevel'. You want to make sure it's integer between 1 and 5. You can't make a rule for it, since you don't want public users to be able to assign in when creating User instance. So you have no way of enforcing data integrity in the database, unless you write your own validation outside the rules().
But if that's the way it will be implemented, can we at least be able to mark the rule 'unsafe' - so we can still make a rule even for those unsafe attributes.
I agree that we may have 'unsafe' rule to make the whole thing complete. It is very rare though that we would need to use 'unsafe' rule in practical applications. For the 'securityLevel' example you gave, it would mean we do not trust the explicit assignment we have for the 'securityLevel' attribute and would use some rules to ensure the data sanity even though the data is set using an explicit assignment.
The only thing that bothers me is that sometimes I currently might define a rule with no scenario even though that rule does not actually apply to all scenarios. For instance I may set an email attribute to use the email validator. This way, the email attribute will always validate as an email address when it is needed to
This way I don't have to assign the email rule to the email attribute for every scenario that uses it
But with this new implementation now it will always be considered 'safe' if I do this technique
Let me summarize: we would like attributes that can’t be massively assigned.
Otherwise, a distressing security hole would be present.
–
Can I suggest not to implement these validating rules via methods, but public properties. I’ve seen numerous methods returning arrays and string that can be stored in class properties. This shouldn’t be a performance consideration, but semantically, using methods to store data is wrong, I guess. Am I right?
@Jonah: I can see your point. The fact is: with the current 1.0 implementation, 'email' is safe by default because it is not a PK attribute. Unless you define safeAttributes() that explicitly excludes 'email' from the safe list for the particular scenarios, the new scheme will not be less secure (it is actually more secure in most cases).
@pestaa: the reason to use method to return rules is to allow dynamic customization. For example, you may want to use Yii::t() to customize the error message. This is not possible with public properties.
Yes, I do have safeAttributes() customized to do that. I suppose it will work fine your way, personally I think over all it will require typing a longer rules() list, and you will have to be careful about adding new scenarios so that all the appropriate rules are assigned to it
You are totally right. I wasn’t familiar with this limitation.
What about declaring these arrays in the constructor and storing them in public properties anyway? I wanted to stick to semantically correct ways, so here’s my golden path.
In this case, discussion about merging methods (which can be confusing in the end because of endless configuration possibilities) won’t be necessary, since the data is in the properties. (Simplifying their declaration may be still needed, though.)
Remove safeAttributes() and use validation rules to decide whether an attribute is safe or not.
Add 'safe' and 'unsafe' validators.
Additionally, I removed the $scenario parameter from all methods requiring it. Instead, we use $model->scenario to decide what the current scenario is. This makes the APIs much cleaner and more consistent.
We will keep attributeLabels() as is.
Please let me know if you find any problems with these new implementations. The code is in trunk (not 1.0 branch).
Qiang, could you post an example code applying this new approach?
Maybe could be possible to develop an script to add the actual safeAttributes to the end of the rules method. This should make this BC-break less critical.
Great improvements, Qiang, thank you for all the efforts!
What’s the difference between applying unsafe validator and not applying any safe/unsafe validators at all? Why not assume that all fields are unsafe unless explicitly marked as safe? Or do they already have a default behavior?