Hide / show editor fields based on session permission

Hide / show editor fields based on session permission

TronikTronik Posts: 122Questions: 28Answers: 1

Hi,

I know I can set read/write access in the server-side script based on current user session, but I also need to completely hide a few fields based on the same session.

Is there any built in functions for this or what would the best practice to achieve this be?

This question has accepted answers - jump to:

Answers

  • kthorngrenkthorngren Posts: 21,325Questions: 26Answers: 4,949

    There is nothing built-in for this. The most secure option is to return only the fields desired from the server script. This way the data never resides at the client.

    However you can use columns.visible to initially hide those columns then use column().visible() to display them if the permissions allow. Do this in initComplete. With this solution the hidden data resides at the client and a hacker could find it.

    Kevin

  • rf1234rf1234 Posts: 2,990Questions: 87Answers: 421
    edited January 2023 Answer ✓

    If you don't want to let those fields reach the client the safest way is to use a getFormatter on the server and return empty values or some other value for those fields depending on the user session.

    Field::inst( 'fobiddenFieldFor007' )
        ->getFormatter( function($val, $data, $opts) {
            if ( $_SESSION['id'] == 7 ) {
                return "___forbidden___";
            }
            return $val;
        })
        ->setFormatter( function($val, $data, $opts) {
            if ( $_SESSION['id'] == 7 ) {
                return ""; // or null or whatever you like
            }
            return $val;
        }),
    

    On the client side you can use the "open" event for example and hide the fields a) when they are empty or b) when they contain a special value returned from the server (e.g. "forbidden", "hidden" or whatever you find suitable).

    editor
        .on( 'open', function () {
            if ( this.val('forbiddenFieldFor007') === "___forbidden___" ) {
                this.field('forbiddenFieldFor007').hide();
            } else {
                this.field('forbiddenFieldFor007').show();
            }
        })
    

    This way the client doesn't need to know about the reason for hiding the Editor fields and you don't send "critical" data to the client either.

    Hey Kevin, I thought the question was about Editor. If it is about table columns as well, of course "columns.visible" is the way to go.

  • kthorngrenkthorngren Posts: 21,325Questions: 26Answers: 4,949

    Missed the Editor part in the title :smile:

    Kevin

  • rf1234rf1234 Posts: 2,990Questions: 87Answers: 421

    I think the question is relevant for the data table as well: If the information is not permitted for the respective user you don't want to show it neither in Editor nor in the Data Table ...

  • TronikTronik Posts: 122Questions: 28Answers: 1

    Thank you,

    Without having tried your approach rf1234, I assume the hidden/forbidden fields actual value will be unchanged if the user edits an allowed field?

  • rf1234rf1234 Posts: 2,990Questions: 87Answers: 421
    edited January 2023 Answer ✓

    Without having tried your approach rf1234, I assume the hidden/forbidden fields actual value will be unchanged if the user edits an allowed field?

    Yep, the hidden field should just be sent back and forth without any client side changes. If theoretically a hacker changed the value sent to the server this wouldn't matter because the setFormatter checks the session variable and not the field content to decide how to update the database.

    If you wanted to avoid any database update for the hidden fields for the specific user you can't use a setFormatter. You would need to use 'validatedCreate' and 'validatedEdit' for example like this:

    Field::inst( 'fobiddenFieldFor007' )
        ->getFormatter( function($val, $data, $opts) {
            if ( $_SESSION['id'] == 7 ) {
                return "___forbidden___";
            }
            return $val;
        })
    
    ...
    ->on('validatedCreate', function ( $editor, $values ) {
        if ( $_SESSION['id'] == 7 ) {
            $editor->field('fobiddenFieldFor007')->set( false );
        }
    })
    ->on('validatedEdit', function ( $editor, $id, $values ) {     
        if ( $_SESSION['id'] == 7 ) {
            $editor->field('fobiddenFieldFor007')->set( false );
        }
    })
    
    
  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin

    I'd also say you should generate your Javascript based on the permissions allowed. Don't have the fields / columns defined for data that the user isn't allowed to access.

    Allan

  • TronikTronik Posts: 122Questions: 28Answers: 1

    Allan, can you exemplify that?

  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin

    Sure - normally with PHP you would create an HTML file. But there is nothing to stop you from creating any other file as well - often it is used for creating thumbnails for example. You can also create Javascript files. In fact, I do it on this site to get some basic information into Javascript land (which then allows the main Javascript to be static and therefore well cached).

    It is more work for certain, but it does allow for a more complete UI since the user will only see what they are allowed to do.

    An example would be creating the fields for the form - use add() to add the fields based on a PHP loop:

    $fields = [ 'name', 'location', 'age' ];
    
    for ($i=0 ; $i<count($fields) ; $i++) {
      echo "editor.add({data: '${fields[$i]'});";
    }
    

    Would produce:

    editor.add({data: 'name'});
    editor.add({data: 'location'});
    editor.add({data: 'age'});
    

    Obviously you can get so much more complex than that, including defining types, labels and so on, based on a database or session query, but that's the basic idea.

    Taking it to the extreme you end up with something like CloudTables :)

    Allan

  • TronikTronik Posts: 122Questions: 28Answers: 1

    Thank you for the clarification :)

    Although Im a little confused with what code would go in which file, since you use hybrid code, echo:ing javascript code with PHP.

    I use the standard datatables JS file along with the PHP server side file

  • rf1234rf1234 Posts: 2,990Questions: 87Answers: 421

    @allan
    if you want to be 100% flexible and all of this must be configurable (as required for Cloud Tables for example) I guess your approach is certainly the best. For my use cases adding all Editor fields dynamically would be a bit too much effort for the few fields in question.

    In most instances I only hide those fields client side if not relevant for certain user groups (and the fields are not containing any "secrets"). Or I only return dummy values from the server for those users (if confidentiality is affected).

  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin

    Yup - that sounds like a good approach I'd say!

    Allan

  • TronikTronik Posts: 122Questions: 28Answers: 1
    edited January 2023

    Thank you both for you answers,
    I think I'll go with the method provided by rf1234 since it works for my use case.

    It would be really nice if the "forbidden" fields also were disabled - shown with a "forbidden" value but not editable. But I guess that is not possible to achieve server-side?

  • rf1234rf1234 Posts: 2,990Questions: 87Answers: 421
    edited January 2023

    It would be really nice if the "forbidden" fields also were disabled - shown with a "forbidden" value but not editable. But I guess that is not possible to achieve server-side?

    No, not serverSide, but quite easily client side.

    You can either not show the fields to the user at all as in my code above. Here it is again:

    editor
        .on( 'open', function () {
            if ( this.val('forbiddenFieldFor007') === "___forbidden___" ) {
                this.field('forbiddenFieldFor007').hide();
            } else {
                this.field('forbiddenFieldFor007').show();
            }
        })
    

    or you display something and disable it:

    editor
        .on( 'open', function () {
            if ( this.val('forbiddenFieldFor007') === "___forbidden___" ) {
                this.set( {'forbiddenFieldFor007': 'unauthorized'} );
                this.field('forbiddenFieldFor007').disable();
            } else {
                this.field('forbiddenFieldFor007').enable();
            }
        })
    

    Since you are NOT checking the field value sent to the server for "forbidden" serverSide, it doesn't matter that the field content is now "unauthorized". "unauthorized" will never be saved to the database and not be returned to the client from the server either.

    Particularly if you have multiple fields this

    this.set( {'forbiddenFieldFor007': 'unauthorized'} );
    this.field('forbiddenFieldFor007').disable();
    

    can be rewritten as:

    this.set( {'field1': 'unauthorized', 'field2': 'unauthorized'} )
        .disable( ['field1', 'field2'] );
    
  • TronikTronik Posts: 122Questions: 28Answers: 1

    Thank you very much for your help!

Sign In or Register to comment.