Protected/Private properties missing

All protected/private properties will missing when reload component.

I’m planning make a PR for it.

Solution here is store protected/private properties in Session.

Any other suggestions?

All protected and private properties you have to pull out of public properties each time you need to render them in component. You can use database, session, cache or whatever.
For example if you are showing a user profile in your component you have 1 public property 1 protected property for the user. Here is a pseudo code:

class Profile extends Component
{
  public $user_id
  protected $user;

  public function mount(User $user)
  {
    $this->user_id = $user->id;
    $this->user = $user;
  }

  protected function load()
  {
    if(!$this->user) $this->user = User::findOrFail($this->user_id);
  }

  public function render()
  {
    $this->load();
    return view('users.profile)->with('user, $this->user);
  }

This way you do not reload user on first rendering, but reload on further renderings.
I hope this helps.

Only your public properties will be automatically passed through and available to the front end. This is by design. Your protected/private methods and properties wouldn’t be accessible outside of their scope in php anyway so there’s really no reason to expose those to the front end as well. If you need a protected property to be accessible, you should pass it down to your view on render.

Actually this is a point where I still do not feel myself comfortable with Livewire.
It is always a question what if the user will change public property to some other ID? What if the user will call a public method from JS which supposed to be called only in special cases (like delete something?)

Is there a way at least to prevent changing specific public property?

This is an issue regardless of the ease of accessibility or framework. If you have an api with an endpoint, you need to secure it from these kinds of vectors.

Right. The difference is that in “standard” Laravel there are good practices how to deal with those issues, how to decompose issues into right prortions of separate solutions. In Livewire you are basically trying to pioneer something yourself. Of cause many things can be borrowed from Laravel, but still… Livewire components are so easy to use that programmers might forget to: validate inputs and authorize resources. And those steps need to be repeated before every rendering.

But the idea to use Cache or Session to store inaccessible for the browser variables not bad at all. Component needs to authorize those variables once and keep public just a reference to stored variables. This could be an option to make component more secure without making too many validations / authorizations every time.

That was done by design, a PR for it will just get rejected. Protected and private properties have never been passed to the front end. In the early stages of livewire, protected properties were automatically stored to the cache but there were syncing issues if a user left the page and came back to it with the browsers back button; Protected properties would be restored from the cache, but public properties would be reset to defaults, which could lead to weird out of sync issues. I think browser caching had some involvement too, but I’m kinda hazy on the exact details.

The reason for caching them in the first place was because there wasn’t a built in way yet for how to handle eloquent models, which livewire wouldn’t let you pass to the front end mainly for security issues and the difference between php and javascript objects. Once that functionality was added, that’s when the caching was removed. So now you have to do it manually and it’s on you now to handle those syncing issues if that’s the route you want to take.

All that said, depending on what you are trying to build, there’s probably another approach you can take to avoid it.

Actually there is more simple solution to easy fix at least some issues by including “protection” of properties.
Livewire has some undocumented feature lockedModelProperties array property. Putting property name in this array allows to make the public property unchangeable by JS! See trait HandlesActions.

And actually right in this trait I see another possible improvement of Livewire. I do not see a practical need in the property updating() hook as it is programmed now, but! it would make a lot of sense to decline property change in this hook. Like this:

  protected function callBeforeAndAfterSyncHooks($name, $value, $callback)
  {
        $propertyName = Str::before(Str::studly($name), '.');
        $keyAfterFirstDot = Str::contains($name, '.') ? Str::after($name, '.') : null;
        $beforeMethod = 'updating'.$propertyName;
        $afterMethod = 'updated'.$propertyName;

       // ##### changes:  #####
        if($this->updating($name, $value) === false) return;

        if (method_exists($this, $beforeMethod)) {

             if($this->{$beforeMethod}($value, $keyAfterFirstDot) === false) return;
        }
1 Like

Just tested - it works! This will not allow to change project_id from JS:

     public function hydrate()
     {
           $this->lockPropertyFromSync('project_id');
     }

Genuine question: What is a real world example for using this? I’ve been thinking about this, and maybe it’s just my approach, but the only things I set as public properties (on public facing pages) are things that are intended to be edited by a user. I haven’t personally come across a situation where there is something opened up to the front that I needed to then additionally protect myself against. I’ve scaled back what I actually use livewire for, so I’m just doing basic dom manipulation and form stuff with it now. Mainly admin area stuff, forms, and table sorting type things.

Real example: a user might have restrictions to see particular projects. This way you can authorize user to see project in controller and do not allow to manipulate project in Livewire component - project is authorized and you do not need to authorize it again and again.

I don’t think I follow. If the user isn’t supposed to see it, the component shouldn’t be rendered to begin with?

My question was focused on a piece of data assigned to a public property in a component that should never be able to be edited. Apologies I didn’t word it as clear as I could’ve.

Right, so basically you have tables User, Project and UserProject (relation whether user can access project) and a policy ProjectPolicy that reads from table UserProject the fact that user is allowed to see project. So what you usually do is to have in controller a general clause:

class ProjectController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(Project::class, 'project');
    }

And the project is always authorized in controller this way. But in the Livewire user can open console and put any project_id to the livewire property. So you need to authorize project each time you re-render component in Livewire.

Or you protect the property project_id and “hope” that project was already authorized and user is not able to modify project_id.

Ok, so ignoring that you are storing an id to a public property instead of the model itself,

isn’t true. If the user does that, the page will error out with the checksum error (I forget the exact wording).

Actually, it isn’t true in the way that I know how to change it in the console, did you find another way? I have only found one way to bypass that security measure and it’s not a console hack. I’ve also never posted about it (or seen anyone else) because I don’t think there’s a way to fix it, but if you are validating properly it’s not something to worry about.

It is not about hacking. You just typing in JS console what is well documented in Livewire:

let component = window.livewire.find('the-unique-component-id')

    var title = component.get('title')
    // Gets the current value of the `public $title` component property.
    // Which defaults to '', so `title` would be set to an
    // empty string initially.

    component.set('title', 'Some Title')
    // Sets the `public $title` component property to "Some Title"
    // You will actually see "Some Title" fill the input field
    // on the page. To Livewire there is no difference between
    // Calling this method and actually typing into the input field.

    component.call('create')

And you can emit events in the same way and so on. By protecting particular property this method will not work for this protected property.

I never stored the whole model in the properties :slight_smile:

And here I was trying to do it the hard way lmao.

You probably should, I think it will be harder to change but I never experimented with it. It also saves you manual work when hydrating your component, and keeps track of relationships dynamically. It’s not actually storing the model, just what it needs to get it out of the database for you.

  • The model name/namespace
  • Database connection name
  • Id
  • Loaded relation names

That aside and going back to my original question, do you have another example? I personally (almost) never expose database id’s to the client, any other case you can think of with a public property that should never be edited?

It is very simple. I just typed in console:

window.livewire.find('iKhLXMK0QXiuSZuz8DNL').set('search', 'test');

and it changed the “search” property to “test”.
So can you imagine that user can change your model name and model space any time? Or database connection name? :wink:
If you show your public properties maybe I can give you another example more tailored to your world :slight_smile:

That wouldn’t work :grinning:, I don’t expose my models or ids to the javascript side, and the only thing I put to public properties are thing’s I’m expecting the user to have the ability to change. Everything else is passed to the view compiler in the render method, and anything that needs to be stateful is saved in the session.

That is the reason I was asking, because I’m of the mindset that only the things that should be editable should be public properties, and I was looking to see if anyone had an example that proved it wrong.

But wait, what is the 3rd item here?:

I don’t use that, I was just saying that by doing so livewire automatically hydrates them for you instead of manually doing it. I also said I thought that it might be harder to change than just storing the id, but I have no basis to know if that’s right or not. Livewire uses the lockPropertyFromSync() method you posted about earlier on it when you do it that way https://github.com/livewire/livewire/blob/master/src/HydrationMiddleware/HydrateEloquentModelsAsPublicProperties.php.

1 Like