0

I have a NodeJS / Express RESTful API that proxies requests from an Active Directory LDAP Server. I do this because LDAP queries tend to be slow. I use the RESTful API to cache and refresh data intermittently. I recently attempted to add the thumbnail photo. In research it appears the library that I am using ldapjs is converting the native ldap byte array to a string.

Example of what this looks like:

\ufffd\ufffd\ufffd\ufffd\u0000\u0010JFIF\u0000\u0001\u0000\u0001\u0000x\u0000x\u0000\u0000\ufffd\ufffd\u0000\u001fLEAD Technologies Inc. V1.01\u0000\ufffd\ufffd\u0000\ufffd\u0000\u0005\u0005\u0005\b\

Due to this fact the image does not render correctly on the angular client app. So based on my research, here are some attempts in correcting this problem:

html binding:

<div>
  <img *ngIf="userImage" [src]="userImage" alt="{{dataSource.sAMAccountName}}">
</div>

controller:

public get userImage() {

  let value = null;
  if(this.dataSource.thumbnailPhoto) {

    const byteArray = this.string2Bin(this.dataSource.thumbnailPhoto);
    const image = `data:image/jpeg;base64,${Buffer.from(byteArray).toString('base64')}`;
    value = this.domSanitizer.bypassSecurityTrustUrl(image);
  }
  return value;
}

private string2Bin(str) {
  var result = [];
  for (var i = 0; i < str.length; i++) {
    result.push(str.charCodeAt(i));
  }
  return result;
}

and

alternate version of controller:

public get userImage() {

  let value = null;
  if(this.dataSource.thumbnailPhoto) {
    const byteArray = new TextEncoder().encode(this.dataSource.thumbnailPhoto);
    const image = `data:image/jpeg;base64,${Buffer.from(byteArray).toString('base64')}`;
    value = this.domSanitizer.bypassSecurityTrustUrl(image);
  }
  return value;
}

another alternate version of controller:

public get userImage() {

  let value = null;
  if(this.dataSource.thumbnailPhoto) {
    const blob = new Blob( [Buffer.from(this.dataSource.thumbnailPhoto).toString('base64')], { type: 'image/jpeg' } );
    const value = window.URL.createObjectURL(blob);
    return value;
}

I expected a rendered image on the angular page but all I get is the non-rendered placeholder.

Here are the versions of the libraries I am using

  • Angular - 8.0.3
  • NodeJS - 10.15.0
  • ldapjs - 1.0.2

I am sure I am missing something, I am just not sure what it is. Any assistance would be appreciated.

thxmike
  • 614
  • 1
  • 7
  • 25
  • How is `LEAD Technologies Inc. V1.01` in your image bytecode? are you sure it is a image? Can you check with another image and coverting it to buffer then base64 and see if it works? – Aritra Chakraborty Jul 03 '19 at 14:03
  • In regards to your first question? This is the conversion of a byte array to a string by the ldapjs library. This is documented in one of the URI's I provided. https://csjdpw.atlassian.net/wiki/spaces/~Genhan.Chen/pages/235044890/Display+LDAP+thumbnail+photos. In regards to,"are you sure it is a image?" This is directly from the library ldapjs. In regards to your last question "Can you check with another image and coverting it to buffer then base64 and see if it works?" I will put together a test to see if it something with the source. Thanks for the guidance and recommendation. – thxmike Jul 03 '19 at 17:04

1 Answers1

2

So after some guidance provided by @Aritra Chakraborty, I checked the RESTful api source code. It appears to be a problem with a ldapjs library. When using the entry object conversion, it is doing something strange with the data to which it is not usable. I then realized, I had access to the entry raw format which is the byte array . Instead of trying to convert to base64 on the client, I moved this to the API. Then just mapped it back on the client binding and bang it worked.

Here is some example code:

RESTFul api

_client.search(this._search_dn, opts, (error, res) => {
          res.on("searchEntry", (entry) => {

            let result = {};

            result.id = string_service.formatGUID(JSON.parse(JSON.stringify(entry.raw)).objectGUID);
            result = Object.assign({}, result, entry.object);
            if(entry.raw.thumbnailPhoto) {
              result.thumbnailPhoto = entry.raw.thumbnailPhoto.toString('base64');
            }
// The previous 3 lines did not exist previously

On the Angular 8 client I simplified the binding:

public get userImage() {
  let value = null;
  if(this.dataSource.thumbnailPhoto) {
    const image = `data:image/jpeg;base64,${this.dataSource.thumbnailPhoto}`;
    value = this.domSanitizer.bypassSecurityTrustUrl(image);
  }
  return value;
}

I hope someone finds some value out of this.

thxmike
  • 614
  • 1
  • 7
  • 25
  • Great flash of insight to use the entry raw format. I'd been banging my head against a wall for some time with this one ... – jackadams49 Jul 30 '22 at 19:48