Credentials & Editable Lists

Thx to @marcus-j-davies and @Steve-Mcl I got sufficient understanding of editable lists to update my CLoudflare DDNS Node.

One challenge remains:

Currently I store the values in this.spokes, an array of objects. Ideally I would like to save them as credentials (e.g. JSON String). When I define a credential as type: 'text' and use an accordingly named input field, the value shows up.

I understand valyes of type password are not available in the UI, but I use text here.

However I can't seem to get hold of the value in oneditprepare or oneditsave.

What do I miss?

Clarification (Dec26):
The challenge is onEditPrepare of an existing node. The this.credentials.token object doesn’t carry the values, so when you had 10 tokens, you would need to reenter all 10 token values

The password type will never be.

I'd probably store the hosts in a regular array (setup in the defaults) and the store the passwords (in the credentials object) using the host name as the key to the value part which would be the password.

Did you use something like this.credentials.spokes?

Credentials objects are stored in this.credentials

this.credentials has the credentials in the js file as expected. In the html file it only has some Boolean flags. But there must be a mechanism since an input element gets populated when it is type text

That’s the part I struggle with details. The “password” (token in my case) are not too sensitive, so if I get to the point where they are not stored in the flow, I’m good.

The challenge: I might have multiple hosts but I only want to change one of the passwords. Something like

let hosts = this.hosts ?? ['one’:’two’];
let tokens = this.credentials.token ?? {};
editableList.forEach(item => { tokens[item.getHost()] = item.getToken()});
this.credentials.token = tokens;

token is defined as credentials type text (skipped JSON.parse/string parts and the item functions).

But I must be missing something ?

P.S I really appreciate your time spent helping me

Something like:

function saveList() {
    const items = $('#node-input-spokes-container').editableList('items');
    const node = this;
    node.spokes = [];
    node.credentials.token = {};

    items.each(function () {
        // Or use id instead of class
        const index = $(this).data("data")?.index;
        const curHost = $(this).find('.datahost').val(); // -> `#host${index}`
        const curToken = $(this).find('.datatoken').val();
        node.spokes.push(curHost);
        node.credentials.token[curHost] = curToken;
    }
}

That works for initial collection (or when you deal with a non-credential property. It doesn’t work in oneditprepsre when updating

It won't work because as we already know, credentials are never passed back - only a placeholder value of _PASSWD_ (or something similar) is ever sent back to the client.

  1. Don't initialise token to and empty object.

  2. Dont update any token that where its value would become the placeholder value.

TIP.
In your existing code, put a debugger statement before the update of token & inspect it in the browsers console to see what the value of curToken is for unaltered passwords. Then alter your code to skip setting that entry if the curToken is that placeholder value.

You want to use text cred type, so the value is real - it is not hidden by __PWRD__
Use the debugger to see the value of this.credentials in oneditprepare

Hmmm. When I set credentials: { test: {type = „text“ }} I get clear text back when linking it to an input field. Only type=password gets the placeholder. I shall take your pointers and debug a little more and report back.

Thx for chipping in!

Sorry, I'm late to this party.
I think reading all the comments above, would this work?

Add a new / update host (during oneditsave)

  1. only add to creds if not existing already
if(!this.credentials.token[item.host]){
  this.credentials.token[item.host] = item.token
}
  1. Update only if not __PWRD__ (or do it based on it being updated in the DOM, as per @Steve-Mcl I think?)
if(item.token !== '__PWRD__'){
  this.credentials.token[item.host] = item.token
}

Removing a token from creds

  1. Employ the removeItem callback in the editableList initialiser
removeItem: RemoveItem
  1. Remove Entry from Creds
function RemoveItem(item) {
    delete this.credentials.token[item.host]
}

I may have this wrong with what you are trying to do, but hopefully may help in finding an answer.

EDIT
Probably not actually, as this.credentials.token will be retuned as __PWRD__ when opening it up :pensive:

Yeah. Will need to go back to my original suggestion...

For this pattern to work.

1 Like

Investigation results

So I did some experiments:

token as text credential

{
   credentials: { token: {type: 'text'} },
   oneditprepare: loadList,
   oneditsave: saveList
}

Treating the credentials as a JSON string to be parsed/stringified

  function loadList() {
    $('#node-input-spokes-container')
      .css('min-height', '150px')
      .css('min-width', '450px')
      .editableList({
        removable: true,
        header: $('<div>').append('<p>Configured host list</p>'),
        addButton: 'Add Spoke',
        addItem: addSpoke
      });
    debugger;
    let candidate;
    try {
      candidate = JSON.parse(this.credentials.token);
    } catch (e) {
      console.error(e);
      candidate = [];
    }
    candidate.forEach((spoke) =>
      $('#node-input-spokes-container').editableList('addItem', spoke)
    );
  }

  function saveList() {
    const items = $('#node-input-spokes-container').editableList('items');
    debugger;
    const newSpokes = [];
    for (const element of items) {
      const El = $(element);
      const curHost = El.find('.datahost')[0].value;
      const curToken = El.find('.datatoken')[0].value;
      newSpokes.push({ host: curHost, token: curToken });
    }
    this.credentials.token = JSON.stringify(newSpokes);
  }

Observed outcome

  • kinda works
  • didn't trigger the "dirty" marker. only can deploy if any other value is updated

token as text credential - take two

added some html

  <div class="form-row">
    <label for="node-input-token"> Token</label>
    <input type="text" id="node-input-token">
  </div>

and amended the js

$('#node-input-token')[0].value = JSON.stringify(newSpokes);

Observed outcome

  • JSON string shows up in edit field
  • Dirty indicator works

I'll run with this for now and shall test the other proposed approaches at a later time and report back

Thx for all contributions and pointers

You can set the dirty indicator manually if you need to.

1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.