Login with IBM i profile

Issue #2 open
Aaron Bartell created an issue

Need to lock down access to this app so shops can have this running and be assured only authorized profiles have access. Best to use IBM i profiles to login and then also use them on subsequent calls to the DB.

Comments (24)

  1. Brian Garland

    I think I can take this on.

    Were you thinking the prompting would be a basic form and if db.conn() was successful that would be enough to get to the dashboard? Once there normal IBM i security would apply to any of the queries.

    I'll have to do a little research on the proper way to pass user ids and passwords around but other than that I think this is within my capabilities.

  2. Aaron Bartell reporter

    Were you thinking the prompting would be a basic form and if db.conn() was successful that would be enough to get to the dashboard?

    I hadn't thought of doing it that way, but that may work. I was thinking of using the QSYGETPH API to authenticate.

    The other thing I hadn't yet figured out is how to store the user/password in a session - enough that it is secured and not shared with anyone else's session.

    Thoughts?

  3. Brian Garland

    Oh, of course. I have used that API several times.

    In the past I used it in conjunction with QWTSETP to then change to that user. I'm not sure how that would work in a Node.js application.

    The API could be used to verify the user/password and then you would still have to do a db.conn() to make any SQL calls.

    I am still researching the safe way to do the user/password storage either. I will post my thoughts when I have some ideas.

  4. Brian Garland

    My "how to do a login" search came up with JSON Web Tokens. I have done some testing with a sample program (just validating against a table) and it seems to be working.

    The issue with this is that subsequent calls would not have the user/password to do the db.conn() with. If we need to do that then we may have to come up with a home grown solution which is not always he most secure.

  5. Aaron Bartell reporter

    Thinking out loud...

    Because the Node.js DB2 for i connection API only allows a string user and password that then forces us down specific paths. For example, because of this I don't believe we can use IBM i security tokens.

    This also means one-way hashes can't be used because we still need the actual password.

    In the end we need to store the user and password in the session which means we need to think highly of security, which means encryption of some sort. This thought process led me to some different Google searches and turned up client-sessions.

    client-sessions should work, I think. Below is sample usage:

    var sessions = require("client-sessions");
    app.use(sessions({
      cookieName: 'mySession', // cookie name dictates the key name added to the request object
      secret: 'blargadeeblargblarg', // should be a large unguessable string
      duration: 24 * 60 * 60 * 1000, // how long the session will stay valid in ms
      activeDuration: 1000 * 60 * 5 // if expiresIn < activeDuration, the session will be extended by activeDuration milliseconds
    }));
    

    It would be necessary to load the secret: property from an external file that each shop would configure and be responsible for securing.

    ##Thoughts?

  6. Brian Garland

    I never replied to your client-sessions suggestion. I just ran with it!

    It's been a busy couple of weeks, but I did manage to fit in some time on this and make some progress. I have a stand alone app with client-sessions working. It does not validate against the OS yet, but otherwise it seems to work.

    Now to read up on XMLSERVICE so I can call the API to validate the entered user profile and password.

  7. Brian Garland

    Well, I've been away from this for a while but I thought I was close when I had to put it aside. Now as I try to pick it back up I'm getting a weird error.

    When I tried calling the QSYSGETPW API from node I was getting a similar error so I wrapped it in RPGLE to simplify the parameters and I was sure I had it working. Today when I picked it up I had a couple of simple changes and now it is failing calling the RPGLE program.

    Any idea what is causing this:

    SetEnvAttr() attr = 10004, value = 1, rc = -1
    
     **** ERROR *****
    SQLSTATE: HY010
    Native Error Code: -99999
    Error occurred in SQL Call Level Interface
    [Error: SQLSTATE=HY010 SQLCODE=-99999 Error occurred in SQL Call Level Interface]
    

    Here's some code snipets:

      var pgm = new xt.iPgm("CHKPWD",{"lib":"EMPBJG00"});
      pgm.addParam(req.body.UserName, "10A");
      pgm.addParam(req.body.Password, "10A");
      pgm.addParam(" ", "1A");
      conn.add(pgm.toXML());
      function my_call_back(str) { 
        var results = xt.xmlToJson(str)
        results.forEach(function(result, index){
          result.data.forEach(function(data, index2){
            console.log("type:" + data.type + " value:" + data.value);
          });
        });
      };
      conn.run(my_call_back);
    
         H DFTACTGRP(*NO) ACTGRP(*NEW)                                                
    
         D CHKPWD          PR                                                         
         D  User                         10A                                          
         D  Pass                         10A                                          
         D  Valid                         1A                                          
    
         D CHKPWD          PI                                                         
         D  User                         10A                                          
         D  Pass                         10A                                          
         D  Valid                         1A                                          
    
         D QSYGETPH        PR                  EXTPGM('QSYGETPH')                     
         D  User                         10A   CONST                                  
         D  Pass                        128A   OPTIONS(*VARSIZE)                      
         D  Handle                       12A                                          
         D  ErrorCode                 32765A   OPTIONS(*VARSIZE)                      
         D  PassLen                      10I 0 CONST                                  
         D  PassCCSID                    10I 0 CONST                                  
    
         D ErrorDS         DS                                                         
         D  Provided                     10I 0 INZ(%SIZE(ErrorDS))                    
         D  Avail                        10I 0 INZ(0)                                 
         D  MsgID                         7A   INZ(*BLANKS)            
         D  Reserved                      1A   INZ(*BLANK)             
         D  MsgDta                      256A   INZ(*BLANKS)            
    
         D Handle          S             12A                           
    
          /FREE                                                        
    
           *INLR = *ON;                                                
    
           QSYGETPH(User:Pass:Handle:ErrorDS:%LEN(%TRIM(Pass)):0);     
    
           IF Avail > 0;                                               
               Valid = '0';                                            
           ELSE;                                                       
               Valid = '1';                                            
           ENDIF;                                                      
    
           RETURN;                                                     
    
          /END-FREE                                                    
    
  8. Brian Garland

    I was having the same or similar error and I thought it was because I was messing up the parms so I tried something familiar.

  9. Brian Garland

    I went back to my test program:

    var xt = require('/QOpenSys/QIBM/ProdData/Node/os400/xstoolkit/lib/itoolkit')
    var conn = new xt.iConn("*LOCAL")
    var pgm = new xt.iPgm("QSYGETPH",{"lib":"QSYS"})
    pgm.addParam("BRIAN", "10A")
    pgm.addParam("TEST", "10A")
    pgm.addParam(" ", "12A")
    pgm.addParam([ 
        [0,"10i0"],
        [0,"10i0"],
        [" ", "7A"],
        [" ", "1A"],
        [" ", "256A"]
    ])
    pgm.addParam(10, "10i0")
    pgm.addParam(-1, "10i0")
    conn.add(pgm.toXML())
    function my_call_back(str) {
      var results = xt.xmlToJson(str)
      console.log(str)
      console.log(results)
      results.forEach(function(result, index){
        result.data.forEach(function(data, index2){
          console.log("type:" + data.type + " value:" + data.value)
        })
      })
    }
    conn.run(my_call_back)
    

    And this is the result:

    $ node zzcall2.js
    <?xml version='1.0'?><myscript><pgm name='QSYGETPH' lib='QSYS' error='fast'>
    <error><![CDATA[*** error QSYS QSYGETPH ]]></error>
    <version>XML Toolkit 1.9.2</version>
    <error>
    <errnoxml>1100016</errnoxml>
    <xmlerrmsg><![CDATA[XML run pgm failed]]></xmlerrmsg>
    <xmlhint><![CDATA[<pgm name='QSYGETPH' lib='QSYS' error='fast'><parm><data ty]]></xmlhint>
    </error>
    <error>
    <errnoxml>1000006</errnoxml>
    <xmlerrmsg><![CDATA[PGMCALL failed]]></xmlerrmsg>
    <xmlhint><![CDATA[QSYGETPH]]></xmlhint>
    </error>
    <error>
    <errnoxml>1100016</errnoxml>
    <xmlerrmsg><![CDATA[XML run pgm failed]]></xmlerrmsg>
    <xmlhint><![CDATA[<pgm name='QSYGETPH' lib='QSYS' error='fast'><parm><data ty]]></xmlhint>
    </error>
    <jobinfo>
    <jobipc>undefined</jobipc>
    <jobipcskey>FFFFFFFF</jobipcskey>
    <jobname>QSQSRVR</jobname>
    <jobuser>QUSER</jobuser>
    <jobnbr>554131</jobnbr>
    <jobsts>*ACTIVE</jobsts>
    <curuser>BRIAN</curuser>
    <ccsid>37</ccsid>
    <dftccsid>37</dftccsid>
    <paseccsid>0</paseccsid>
    <langid>ENU</langid>
    <cntryid>US</cntryid>
    <sbsname>QSYSWRK</sbsname>
    <sbslib>QSYS</sbslib>
    <curlib></curlib>
    <syslibl>QSYS QSYS2 QHLPSYS QUSRSYS</syslibl>
    <usrlibl>TECHCOMMON VVIPEYE VGPL VIPPHONE QGPL QTEMP</usrlibl>
    <jobcpffind>see log scan, not error list</jobcpffind>
    </jobinfo>
    </pgm>
    </myscript>
    [ { type: 'pgm', success: false, pgm: 'QSYGETPH', lib: 'QSYS' } ]
    [TypeError: Cannot read property 'forEach' of undefined]
    $
    

    How do I find out what this error is?

  10. Brian Garland

    An other two weeks with little progress.

    I have a test program working, but when I incorporate it into index.js the call to QSYGETPH is failing with the error I posted back on 3/15.

    Learning the language is easy, learning to debug it is hard!

    See you at NEUGC.

  11. Brian Garland

    I pushed up the working test program (zzcall3.js) and non-working program (index.js) to my Bitbucket project ibmilogin. You should be able to get the source there.

  12. Brian Garland

    Aaron,

    I haven't had any time since NEUGC to look at this, but I received a message from Joachim Gourdon today. He pointed out to me that even in zzcall3.js I am not getting a full and valid XML back from the call. It gets truncated in the middle of the third parameter. He had added a console.log to zzcall3.js to show this. I updated my project with that so anyone could see it.

  13. Brian Garland

    After debugging XMLSERVICE I was able to see why the XML is truncated. The third parameter to the API is a 12a handle that can contains x'00' in any of the positions. strlen() then computes the wrong length.

    Is this an unknown flaw in XMLSERVICE? Is there any way around it other than creating a wrapper over the API call?

  14. Brian Garland

    Joachim Gourdon determined a parameter change that allowed that binary parameter to work without blowing up the return XML. index.js still failed though. A little more digging and he discovered that the HY010 error was a conflict between the DB connection and the connection needed for XMLSERVICE. After commenting out the DB related code that is not needed in my test app everything is working fine. Now to figure out how to have both connections so that we can do some DB access and integrate this change back into ibmidash!

  15. Aaron Bartell reporter

    Going to mark this as resolved by including the below code that works (for me). I put this in a file named ibmi.js.

    Also, to your other concern, you can use a separate db object using the db2 connection pool code described in this article

    function QSYSGETPH(user, pw, cb){
      var xt = require('/QOpenSys/QIBM/ProdData/OPS/Node6/os400/xstoolkit/lib/itoolkit')
      var conn = new xt.iConn("*LOCAL");
      var pgm = new xt.iPgm("QSYGETPH",{"lib":"QSYS","error":"off"})
      pgm.addParam(user.toUpperCase(), "10A")
      pgm.addParam(pw.toUpperCase(), "10A")   
      pgm.addParam("", "12A", {"io":"out", "hex":"on"}) 
      var errno = [
        [0, "10i0"],
        [0, "10i0", {"setlen":"rec2"}],
        ["", "7A"],
        ["", "1A"]
      ];
      pgm.addParam(errno, {"io":"both", "len" : "rec2"});
      pgm.addParam(10, "10i0")
      pgm.addParam(0, "10i0")
      conn.add(pgm.toXML())
      conn.run(function(str) {
        var results = xt.xmlToJson(str)
        cb(null, results[0].success)
      }, true) // <---- set to sync for now because bug in iToolkit.  Hapijs hangs if this isn't done. If not using Hapijs then remove.
    }
    
    exports.QSYSGETPH = QSYSGETPH
    
  16. Brian Garland

    Wow, it's been a while. I really got bogged down on the async issue and then side tracked with work and never came back to this.

    So, are you saying that the whole IBM i user sign on is done, or just the issue I had in my test programs? If it is complete, did you commit the changes to the repository?

  17. Aaron Bartell reporter

    So, are you saying that the whole IBM i user sign on is done, or just the issue I had in my test programs? If it is complete, did you commit the changes to the repository?

    Good point. I am going to re-open this. I don't have immediate time to accomplish the implementation into this repo. I am finalizing an article for MCPress that will include a full implementation including session management with cookies (should be published in two to three weeks). Once that article is live then whoever has time first can implement it.

  18. Log in to comment