Workaround for Python & Selenium: authenticate aga

2019-01-20 02:36发布

问题:

I am using Python (2.7) and Selenium (3.4.3) to drive Firefox (52.2.0 ESR) via geckodriver (0.19.0) to automate a process on a CentOS 7 machine.
I need totally unattended operation of this automation with user credentials passed through; no storage allowed and no breaking in.
One piece of drama is being caused by the fact that the internal website required for the process is within an Active Directory domain while the machine running my automation is not. I have no need to validate the user, only pass the credentials to the website in such a way as to not require human interaction or for the person to be a local user on the machine.

I have tried various permutations of:

  • [protocol]://[user,pass]@[url]
  • driver.switch_to_alert() + send_keys

It seems some of those only work on IE, something I have no access to.
I have checked for libraries to handle this and all to no avail.

I can add libraries to python and I have sudo access to the machine - can't touch authentication, so AD integration is not possible.

How can I give this AD website the credentials of an arbitrary user such that no local storage of their credentials happens an no user interaction is required?

Thank you

EDIT

I think something like a proxy which could authenticate the user then retain that authentication for selenium to do its thing ... Is there a simple LDAP/AD proxy available?

EDIT 2

Perhaps a very simple way of stating this is that I want to pass user credentials and prevent the authentication popup from happening.

回答1:

Solution Found:

I needed to use a browser extension. My solution has been built for chromium but it should port almost-unchanged for Firefox and maybe edge.

First up, you need 2 APIs to be available for your browser:

  • webRequest.onAuthRequired - Chrome & Firefox
  • runtime.nativeMessaging - Chrome & Firefox

While both browser APIs are very similar, they do have some significant differences - such as Chrome's implementation lacking Promises.

If you setup your Native Messaging Host to send a properly-formed JSON string, you need only poll it once. This means you can use a single call to runtime.sendNativeMessage() and be assured that your credentials are paresable. Pun intended.

Next, we need to look at how we're supposed to handle the webRequest.onAuthRequired event.

Since I'm working in Chromium, I need to use the promise-less Chrome API.

chrome.webRequest.onAuthRequired.addListener(
  callbackFunctionHere,
  {urls:[targetUrls]},
  ['asyncBlocking'] // --> this line is important, too. Very.

The Change:

I'll be calling my function provideCredentials because I'm a big stealy-stealer and used an example from this source. Look for the asynchronous version.

The example code fetches the credentials from storage.local ...

chrome.storage.local.get(null, gotCredentials);

We don't want that. Nope.

We want to get the credentials from a single call to sendNativeMessage so we'll change that one line.

chrome.runtime.sendNativeMessage(hostName, { text: "Ready" }, gotCredentials);

That's all it takes. Seriously. As long as your Host plays nice, this is the big secret. I won't even tell you how long it took me to find it!

Links:

My questions with helpful links:

  • Here - Workaround for Authenticating against Active Directory
  • Here - Also has some working code for a functional NM Host
  • Here - Some enlightening material on promises

So this turns out to be a non-trivial problem.

I haven't implemented the solution, yet, but I know how to get there...

Passing values to an extension is the first step - this can be done in both Chrome and Firefox. Watch the version to make sure the API required, nativeMessaging, actually exists in your version. I have had to switch to chromium for this reason.

Alternatively, one can use the storage API to put values in browser storage first. [edit: I did not go this way for security concerns]

Next is to use the onAuthRequired event from the webRequest API . Setup a listener on the event and pass in the values you need.


Caveats: I have built everything right up to the extension itself for the nativeMessaging API solution and there's still a problem with getting the script to recognise the data. This is almost certainly my JavaScript skills clashing with the arcane knowledge required to make these APIs make much sense ... I have yet to attempt the storage method as it's less secure (in my mind) but it does seem to be simpler.