Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ActiveDirectoryPassword and sqlpassword authentication support #707

Merged
merged 103 commits into from
Nov 21, 2018

Conversation

Hadis-Knj
Copy link

squash merged of #661, please see details and previous discussion there.

@Hadis-Knj
Copy link
Author

noticed the latency in generating token in adal-node lib, follow up on AzureAD/azure-activedirectory-library-for-nodejs#192

src/connection.js Outdated Show resolved Hide resolved
@debben debben mentioned this pull request May 21, 2018
Copy link
Collaborator

@arthurschreiber arthurschreiber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Hadis-Fard,

what's the current state of this PR? @David-Engel let me know that there's a bunch of customers waiting for it's integration, so I'm trying to figure out what needs to be done to get it into a shippable state.

I left a few comments for things that jumped out to me, but I also wanted to check with how you feel about these changes. 🙇


const countOfInfoIDs = data.readUInt32LE(infoData.offset);
infoData.offset += 4;
async.times(countOfInfoIDs, () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think async.times usage is required here. The full token data was already loaded into data, and can be processed synchronously here.

@@ -0,0 +1,61 @@
const async = require('async');

function readFedAuthInfoOpt(data, infoData, FEDAUTHINFOID, callback) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be great if this function could be refactored a bit for better readability. It can be a regular non-async method, and I think it should return a { stsurl: '...', spn: '...' } object.

Having it take and modify the infoData object in place is a bit confusing. 🤔

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, FEDAUTHINFOID could be a top-level constant, instead of being passed in as well.

@@ -0,0 +1,27 @@

module.exports = function featureExtAckParser(parser, colMetadata, options, callback) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this parser is quite right. 🤔

According to the MS-TDS spec, the FEATUREEXTACK token can return multiple different features, while this implementation only ever returns the latest feature that was read.

Also, it only returns the feature data length, not the actual data itself.

What do you think about changing the token to return an object with the following structure:

{
  name: 'FEATUREEXTACK',
  event: 'featureExtAck',
  features: [
    {
      id: number,
      data: Buffer,
    },
    ....
  ]
}

@arthurschreiber
Copy link
Collaborator

Oh, and another thing that we'll need before this can be merged is a SQL Server in Azure that we can use for CI testing.

@David-Engel @tediousjs/microsoft-contributors Is there some way to get an instance for testing?

@Hadis-Knj
Copy link
Author

Hadis-Knj commented May 24, 2018

Thanks @arthurschreiber for reviewing this PR . It contains the AAD foundation and support of two auth methods - sqlpassword and AD password, and their unit tests. there are two other auth methods -token based auth and AD integrated auth - to be implemented, but those can be added incrementally in separate PRs after the foundation is merged, which is the same approach we took in implementing AAD in other client drivers.
I will go through your review comments shortly once i'm done with the tasks in hand🙇‍♀️ , about the ci we can go with the idea discussed in #624, @v-suhame could you help in giving the context or ideas you have 🙏 ?

@Suraiya-Hameed
Copy link
Member

Suraiya-Hameed commented May 25, 2018

I have set environment variables in Travis (AZURE_PASS, AZURE_SERVER, AZURE_USERNAME) that has all the credentials needed for CI testing against Azure DB. We can add it as a stage after lint so, we end up testing with Azure only if every other test passes.

    - stage: azure
      node_js: 8
      before_install:
        - mkdir ~/.tedious
        - >
          echo '{
            "config": {
              "server": "'${AZURE_SERVER}'",
              "userName": "'${AZURE_USERNAME}'",
              "password": "'${AZURE_PASS}'",
              "options": {
                "port": 1433,
                "database": "test",
                "encrypt": "true"
              }
            }
          }' > ~/.tedious/test-connection.json
      before_script: skip
      script: npm run test-integration

How does that sound? Also, is it enough to just test with the latest version of node? 🤔

@Suraiya-Hameed
Copy link
Member

We should also fix the test that fails intermittently (test/integration/pause-resume-test.js), it fails every time with remote server 😅
@arthurschreiber Does @chdh's suggestion in #518 (comment) sounds good or did you have anything else in mind?

@arthurschreiber
Copy link
Collaborator

@v-suhame That looks great! Can we add this change to the travis configuration in this pull request here?

@Suraiya-Hameed
Copy link
Member

I forgot AAD had 4 auth mechanisms 😅 The initial credentials I had set was for SQL Server Authentication, I have added 2 more variables for Active Directory - Password authentication (AZURE_AD_USER and AZURE_AD_PASS). For now I can add 2 stages, one to test sqlpassword and other for AD password, we can always update it when we find a better alternative.

@arthurschreiber
Copy link
Collaborator

@Hadis-Fard This should be almost ready to merge now. I had to update some tests to better handle missing permissions and will extract these changes out of this PR, but I think this should be ready otherwise. Would you mind taking another look over the refactorings I made? 🙇

@@ -744,7 +743,7 @@ class Connection extends EventEmitter {
switch (this.fedAuthInfo.fedAuthLibrary) {
case FEDAUTH_OPTIONS.LIBRARY_ADAL:
if (0 !== fedAuthAck.length) {
this.loginError = ConnectionError(`Active Directory authentication acknowledgment for ${this.fedAuthInfo.method} authentication method includes extra data`);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should keep the method property, azure-active-directory should be a basket for various methods of AAD auth, for example security token, msi, and aad integrated auth.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather have individual auth types, like azure-active-directory-integrated. I was not really able to find a lot of documentation on the other two methods, but for those we could also have separate authentication types? 🤔

@@ -739,17 +738,14 @@ class Connection extends EventEmitter {
if (!this.fedAuthInfo.fedAuthInfoRequested) {
this.loginError = ConnectionError('Did not request Active Directory authentication, but received the acknowledgment');
this.loggedIn = false;
}
switch (this.fedAuthInfo.fedAuthLibrary) {
case FEDAUTH_OPTIONS.LIBRARY_ADAL:
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should keep adal library check and FEDAUTH_OPTIONS and ADAL LIBRARY as we'll expand the feature to other auth types

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here as well, I'd rather have this as separate authentication types, instead of mixing this all together into one type with a large set of options.

@Hadis-Knj
Copy link
Author

Hadis-Knj commented Nov 6, 2018

@arthurschreiber in addition to 'azure-active-directory' user should set the method of authentication which could beActiveDirectoryIntegrated, ActiveDirectoryPassword https://docs.microsoft.com/en-us/sql/connect/jdbc/connecting-using-azure-active-directory-authentication?view=sql-server-2017 and also MSI token, each with different underlying implementation, for example, adal-library is only valid for activedirectorypassword, and token based auth with use another lib, see 2.2.6.4 Login7 FedAuth row.

so in the config in addition to azure-active-password we should have another field to set the method as ones of the above,

new Connection({
  "server": "ADServer",
  "authentication": {
    "type": "azure-active-directory",
    "method" : "ACTIVEDIRECOTRYPASSWORD",
    "options": {
      "userName": "ADUser",
      "password": "foobar"
    }
  }
});

and have the FedAuthEnum back in code, check the inclusion and check tds version

 if (this.config.authentication.type === 'azure-active-directory') {      
      if(!Object.values(this.fedAuthInfo.ValidFedAuthEnum).includes(config.authentication.toUpperCase())) {          
        throw new Error(`${config.authentication} is not a valid authentication method`);
       }
 if (this.config.options.tdsVersion < '7_4') {
         throw new Error(`Azure Active Directory authentication is not supported in the TDS version ${this.config.options.tdsVersion}`);
        }

@arthurschreiber
Copy link
Collaborator

arthurschreiber commented Nov 6, 2018

noticed the latency in generating token in adal-node lib, follow up on AzureAD/azure-activedirectory-library-for-nodejs#192

@Hadis-Fard The latency in getting a token was due to the way execution was delayed via recursive setImmediate calls. This was basically filling up the event loop and delaying the token response handling. I made a few refactorings that got rid of this waiting, and this sped up the whole token request process.

@arthurschreiber
Copy link
Collaborator

@Hadis-Fard We could also rename the authentication type to azure-active-directory-password, and have the integrated type be called azure-active-directory-integrated.

@Hadis-Knj
Copy link
Author

👍 to have separate type for each kind of AAD auth. However all other drivers are using the same keyword for AAD methods . I suggest to rename azure-active-directory-password to ActiveDirectoryPassword and so on.
❤️ fixing the latency issue ❤️

@Hadis-Knj
Copy link
Author

@arthurschreiber thanks for working on this 👌, we need to make adjustments and then AAD should be ready to merge:

  • update the AAD Keyword as discussed earlier.

  • "options" in json file after "authentication" entry won't get set.

  "server": "yourServer",
    "authentication": {
      "type": "azure-active-directory",
      "options": {
        "userName": "user",
        "password": "pwd",
    },
    "options": {
      "encrypt": true
    },
  }

in this case encrypt is not set to true and authentication fails with the message that server requires encrypt to be true

@arthurschreiber
Copy link
Collaborator

  • "options" in json file after "authentication" entry won't get set.

@Hadis-Fard there's two options values now, one at the top level configuration and one specific to the authentication that is being used. As can be seen in the Travis CI build, this works as expected. 👍

@arthurschreiber arthurschreiber merged commit 83bf922 into tediousjs:master Nov 21, 2018
@arthurschreiber
Copy link
Collaborator

🎉 This PR is included in version 4.1.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@Hadis-Knj
Copy link
Author

@arthurschreiber setting encrypt in the options object after authentication would raise Server requires encryption, set \'encrypt\' config option to true.' error.

var connectionADnew = {
    server: "ADServer.windows.net",
    authentication: {
      type: "azure-active-directory-password",
      options: {
        userName: "ADuser",
        password: "pwd",
    },
    options: {
      encrypt: true,
    }
  }
};

@arthurschreiber
Copy link
Collaborator

@Hadis-Fard I've trouble reproducing this error? This is exactly how the Travis tests are configured, and how I locally test against Azure DB.

tedious/.travis.yml

Lines 83 to 98 in da45fef

echo '{
"config": {
"server": "'${AZURE_SERVER}'",
"authentication": {
"type": "azure-active-directory-password",
"options": {
"userName": "'${AZURE_AD_USER}'",
"password": "'${AZURE_AD_PASS}'"
}
},
"options": {
"database": "test",
"encrypt": true
}
}
}' > ~/.tedious/test-connection.json

@arthurschreiber
Copy link
Collaborator

@Hadis-Fard I only get the error you mentioned if I don't set encrypt at all (or set it to false). This is expected until #694 is merged (which I hope to do sometime next week).

@Hadis-Knj
Copy link
Author

@arthurschreiber 😕 we don't need to worry about this error that much since as you said it'll gonna get fixed by merging #694

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants