Skip to content

Commit

Permalink
Merge pull request #14 from bjowes/develop
Browse files Browse the repository at this point in the history
Release 0.7.0
  • Loading branch information
bjowes committed Jan 22, 2019
2 parents 5f0d96d + d4246a4 commit da5c096
Show file tree
Hide file tree
Showing 15 changed files with 1,350 additions and 201 deletions.
10 changes: 8 additions & 2 deletions CHANGELOG.md
@@ -1,14 +1,20 @@
# Changelog

## 0.6.0 - released 2019-10-17
## 0.7.0 - released 2019-01-22

* More complete input validation for arguments to cy.ntlm command and better error reporting
* Corrected docs regarding domain and workstation arguments
* More unit tests

## 0.6.0 - released 2019-01-17

* Fixed issue #11 - Requests other then GET are not properly send
* Improved examples in README for Windows users
* More robust handling of invalid states during NTLM handshake
* Validation that NTLM handshake is fully complete
* The Chrome browser sends three odd requests during startup to detect network behavior. These were logged as errors since they are connecting to non-existent hosts. Those errors are now filtered with understandable debug messages.

## 0.5.0 - released 2019-10-10
## 0.5.0 - released 2019-01-10

* Changed termination handling for common handling also on Windows. This means that the ntlm-proxy is no longer terminated from the signals when cypress exits - instead a separate binary ntlm-proxy-exit is provided that will send the quit command to the ntlm-proxy. This can then be executed directly after cypress exits, see updated README.
* Improved handling of hosts on standard ports (80/443)
Expand Down
8 changes: 3 additions & 5 deletions README.md
Expand Up @@ -174,13 +174,11 @@ The ntlm command is used to configure host/user mappings. After this command, al
cy.ntlm(ntlmHost, username, password, domain, [workstation]);
```

* ntlmHost: protocol, hostname (and port if required) of the server where NTLM authentication shall be applied. This must NOT include the rest of the url (path and query). Examples: `http://localhost:4200`, `https://ntlm.acme.com`
* ntlmHost: protocol, hostname (and port if required) of the server where NTLM authentication shall be applied. This must NOT include the rest of the url (path and query) - only host level authentication is supported. Examples: `http://localhost:4200`, `https://ntlm.acme.com`
* username: the username for the account to authenticate with
* password: the password for the account to authenticate with (see [Security advice](#Security-advice) regarding entering passwords)
* domain: the domain for the account to authenticate with (for AD account authentication)
* workstation: the workstation for the account to authenticate with (for local machine account authentication)

The arguments domain and workstation are mutually exclusive. For AD account authentication, set the domain argument but don't set the workstation argument (null or empty string are accepted). For local machine account authentication, set the workstation argument but don't set the domain argument (null or empty string are accepted). If both arguments are set, this command will return an error.
* domain (optional): the domain for the account to authenticate with (for AD account authentication)
* workstation (optional): the workstation name of the client

The ntlm command may be called multiple times to setup multiple ntlmHosts, also with different credentials. If the ntlm command is called with the same ntlmHost again, it overwrites the credentials for that ntlmHost.

Expand Down
56 changes: 37 additions & 19 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "cypress-ntlm-auth",
"version": "0.6.0",
"version": "0.7.0",
"description": "NTLM authentication plugin for Cypress",
"main": "index.js",
"scripts": {
Expand Down Expand Up @@ -43,10 +43,11 @@
"node-cleanup": "^2.1.2"
},
"devDependencies": {
"eslint": "^5.12.0",
"eslint": "^5.12.1",
"express-ntlm": "^2.3.0",
"is-port-reachable": "^2.0.0",
"mocha": "^5.2.0",
"proxyquire": "^2.1.0",
"sinon": "^7.2.2"
"sinon": "^7.2.3"
}
}
25 changes: 25 additions & 0 deletions src/commands/index.d.ts
@@ -0,0 +1,25 @@
/// <reference types="cypress" />

declare namespace Cypress {
interface Chainable<Subject> {
/**
* Adds NTLM authentication support to Cypress for a specific host.
* You can call this multiple times to register several hosts or
* change credentials.
* @example
```js
cy.ntlm('https://ntlm.acme.com', 'TheUser', 'ThePassword', 'TheDomain');
```
*/
ntlm(ntlmHost: string, username: string, password: string, domain?: string, workstation?: string): Chainable<any>

/**
* Reset NTLM authentication for all configured hosts. Recommended before/after tests.
* @example
```js
cy.ntlmReset();
```
*/
ntlmReset(): Chainable<any>
}
}
31 changes: 16 additions & 15 deletions src/commands/index.js
Expand Up @@ -2,17 +2,15 @@

/// <reference types="cypress" />

const configValidator = require('../util/configValidator');

/**
* Adds NTLM authentication support to Cypress for a specific host.
*
* You can call this multiple times to register several hosts or
* change credentials.
* @example
```js
// Enable NTLM auth for a specific host. You can call this multiple times
// to register several hosts or change credentials.
cy.ntlm('https://ntlm.acme.com', 'TheUser', 'ThePassword', 'TheDomain');
cy.visit('/');
// Tests ...
```
*/
const ntlm = (ntlmHost, username, password, domain, workstation) => {
Expand All @@ -27,18 +25,23 @@ const ntlm = (ntlmHost, username, password, domain, workstation) => {
throw new Error('The cypress-ntlm-auth plugin must be loaded before using this method');
}

let result;
let ntlmConfig = {
ntlmHost: ntlmHost,
username: username,
password: password,
domain: domain,
workstation: workstation
};
let validationResult = configValidator.validate(ntlmConfig);
if (!validationResult.result) {
throw new Error(validationResult.message);
}

let result;
cy.request({
method: 'POST',
url: ntlmConfigApi + '/ntlm-config',
body: {
ntlmHost: ntlmHost,
username: username,
password: password,
domain: domain,
workstation: workstation
},
body: ntlmConfig,
log: false // This isn't communication with the test object, so don't show it in the test log
}).then((resp) => {
if (resp.status === 200) {
Expand All @@ -64,10 +67,8 @@ const ntlm = (ntlmHost, username, password, domain, workstation) => {

/**
* Reset NTLM authentication for all configured hosts. Recommended before/after tests.
*
* @example
```js
// Disables NTLM auth for all configured hosts.
cy.ntlmReset();
```
*/
Expand Down
18 changes: 2 additions & 16 deletions src/proxy/server.js
Expand Up @@ -13,6 +13,7 @@ const url = require('url');
const debug = require('debug')('cypress:plugin:ntlm-auth');

const portsFile = require('../util/portsFile');
const configValidator = require('../util/configValidator');

let _ntlmHosts = {};
let _ntlmProxy;
Expand Down Expand Up @@ -69,21 +70,6 @@ function updateConfig(config) {
_ntlmHosts[targetHost] = hostConfig;
}

function validateConfig(config) {
if (!config.ntlmHost ||
!config.username ||
!config.password ||
!(config.domain || config.workstation)) {
return { ok: false, message: 'Incomplete configuration. ntlmHost, username, password and either domain or workstation are required fields.' };
}

let urlTest = url.parse(config.ntlmHost);
if (!urlTest.hostname || !urlTest.protocol || !urlTest.slashes) {
return { ok: false, message: 'Invalid ntlmHost, must be a valid URL (like https://www.google.com)' };
}

return { ok: true };
}

function shutDownProxy(keepPortsFile, exitProcess) {
debug('Shutting down');
Expand Down Expand Up @@ -137,7 +123,7 @@ function startConfigApi(callback) {
_configApp.use(bodyParser.json());

_configApp.post('/ntlm-config', (req, res) => {
let validateResult = validateConfig(req.body);
let validateResult = configValidator.validate(req.body);
if (!validateResult.ok) {
res.status(400).send('Config parse error. ' + validateResult.message);
} else {
Expand Down
64 changes: 64 additions & 0 deletions src/util/configValidator.js
@@ -0,0 +1,64 @@

const url = require('url');

module.exports = {
validate: function(config) {
if (!config.ntlmHost ||
!config.username ||
!config.password) {
return { ok: false, message: 'Incomplete configuration. ntlmHost, username and password are required fields.' };
}

let urlTest = url.parse(config.ntlmHost);
if (!urlTest.hostname || !urlTest.protocol || !urlTest.slashes) {
return { ok: false, message: 'Invalid ntlmHost, must be a valid URL (like https://www.google.com)' };
}
if (urlTest.path && urlTest.path !== '' && urlTest.path !== '/') {
return { ok: false, message: 'Invalid ntlmHost, must not contain any path or query (https://www.google.com is ok, https://www.google.com/search is not ok)' };
}

if (!validateUsername(config.username)) {
return { ok: false, message: 'Username contains invalid characters or is too long.' };
}

if (config.domain && !validateDomainOrWorkstation(config.domain)) {
return { ok: false, message: 'Domain contains invalid characters or is too long.' };
}

if (config.workstation && !validateDomainOrWorkstation(config.workstation)) {
return { ok: false, message: 'Workstation contains invalid characters or is too long.' };
}

return { ok: true };
}
};

// https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/bb726984(v=technet.10)
// Max 104 chars, invalid chars: " / \ [ ] : ; | = , + * ? < >
function validateUsername(username) {
if (username.length > 104) {
return false;
}
if (username.includes('"') || username.includes('/') || username.includes('\\') ||
username.includes('[') || username.includes(']') || username.includes(':') ||
username.includes(';') || username.includes('|') || username.includes('=') ||
username.includes(',') || username.includes('+') || username.includes('*') ||
username.includes('?') || username.includes('<') || username.includes('>')) {
return false;
}
return true;
}

// https://support.microsoft.com/sv-se/help/909264/naming-conventions-in-active-directory-for-computers-domains-sites-and
// Max 15 chars, invalid chars: " / \ : | * ? < >
function validateDomainOrWorkstation(domain) {
if (domain.length > 15) {
return false;
}
if (domain.includes('"') || domain.includes('/') || domain.includes('\\') ||
domain.includes(':') || domain.includes('|') || domain.includes('*') ||
domain.includes('?') || domain.includes('<') || domain.includes('>')) {
return false;
}
return true;
}

0 comments on commit da5c096

Please sign in to comment.