Skip to content
Permalink
Browse files
feat(serenity-bdd): support for internal repositories that require au…
…thentication

If you're downloading the Serenity BDD reporter jar from an internal repositorythat requires
authentication, use the following command: `serenity-bdd update --repository
https://mycompany.com/repo --auth <username>:<password> --ignoreSSL`
  • Loading branch information
jan-molak committed Jun 5, 2020
1 parent 636ab04 commit c14ac29c3fd6257763f26201357721b1a04c72d5
Showing 5 changed files with 85 additions and 15 deletions.
@@ -0,0 +1,29 @@
import 'mocha';

import { expect } from '@integration/testing-tools';
import { Path } from '@serenity-js/core/lib/io';

import { Credentials, GAV } from '../../../src/cli/model';

describe('Credentials', () => {

it('defaults to "no credentials" if the credentials are not specified', () => {
const credentials = Credentials.fromString(undefined);

expect(credentials.username).to.equal(undefined);
expect(credentials.password).to.equal(undefined);
});

it('defaults to "no credentials" if the credentials are empty', () => {
const credentials = Credentials.fromString('');

expect(credentials.username).to.equal(undefined);
expect(credentials.password).to.equal(undefined);
});

it('complains if the credentials string does not follow the <username>:<password> template', () => {

expect(() => Credentials.fromString('invalid'))
.to.throw(Error, 'Credentials should follow the "<username>:<password>" format')
});
});
@@ -1,11 +1,13 @@
import { Check, isTrue } from '@serenity-js/assertions';
import { actorCalled, actorInTheSpotlight, configure } from '@serenity-js/core';
import { Path } from '@serenity-js/core/lib/io';
import { AxiosRequestConfig } from 'axios';
import * as https from 'https';
import { URL } from 'url';

import { Argv } from '../Argv';
import { defaults } from '../defaults';
import { GAV } from '../model';
import { Credentials, GAV } from '../model';
import { Printer } from '../Printer';
import { Complain, DownloadArtifact, FileExists, Notify } from '../screenplay';
import { Actors, NotificationReporter, ProgressReporter } from '../stage';
@@ -20,12 +22,16 @@ export = {
},
ignoreSSL: {
default: false,
type: 'boolean',
describe: 'Ignore SSL certificates',
},
repository: {
default: defaults.repository,
describe: 'Maven repository url where we should look for the Serenity BDD CLI artifact',
},
auth: {
describe: `Credentials to authenticate with your repository - "<username>:<password>"`,
},
artifact: {
default: defaults.artifact,
describe: `The GAV identifier of the Serenity BDD CLI artifact to use; You're best off with the default option unless you want to experiment.`,
@@ -38,7 +44,12 @@ export = {
artifactGAV = GAV.fromString(argv.artifact),
pathToArtifact = new Path(argv.cacheDir).join(artifactGAV.toPath()),
repository = new URL(argv.repository),
httpsConfig = { httpsAgent: new https.Agent({ rejectUnauthorized: ! argv.ignoreSSL }) };
requestConfig: AxiosRequestConfig = {
httpsAgent: new https.Agent({
rejectUnauthorized: ! argv.ignoreSSL,
}),
auth: Credentials.fromString(argv.auth),
};

configure({
actors: new Actors(new Path(process.cwd())),
@@ -58,7 +69,7 @@ export = {
.identifiedBy(artifactGAV)
.availableFrom(repository)
.to(pathToArtifact.directory())
.using(httpsConfig),
.using(requestConfig),
),
)
.catch(error => actorInTheSpotlight().attemptsTo(
@@ -0,0 +1,36 @@
import { ensure, Predicate, TinyType } from 'tiny-types';

/**
* @package
*/
export class Credentials extends TinyType {

static fromString(value: string): Credentials {
if (! value) {
return new Credentials(undefined, undefined);
}

ensure('Credentials', value, matches(/(.*):(.*)/, `follow the "<username>:<password>" format`));

const index = value.indexOf(':');

return new Credentials(
value.substring(0, index),
value.substring(index + 1)
)
}

constructor(
public readonly username: string,
public readonly password: string,
) {
super();
}
}

/**
* @package
*/
function matches(expression: RegExp, description?: string): Predicate<string> {
return Predicate.to(description || `match pattern ${ expression }`, (value: string) => expression.test(value));
}
@@ -1,5 +1,5 @@
import { Path } from '@serenity-js/core/lib/io';
import { ensure, isDefined, isInRange, isInteger, Predicate, property, TinyType } from 'tiny-types';
import { ensure, isDefined, isInRange, isInteger, matches, Predicate, property, TinyType } from 'tiny-types';

/**
* @package
@@ -25,10 +25,10 @@ export class GAV extends TinyType {
public readonly classifier?: string,
) {
super();
ensure('groupId', groupId, isDefined(), matchesRegex('group name', /^[a-z][a-z0-9_-]+(?:\.[a-z0-9_-]+)+[0-9a-z_-]$/));
ensure('artifactId', artifactId, isDefined(), matchesRegex('artifact name', /^[a-z0-9_-]+$/));
ensure('version', version, isDefined(), matchesRegex('version', /^(?:\d+\.?){3}$/));
ensure('extension', extension, isDefined(), matchesRegex('version', /^[a-z]+$/));
ensure('groupId', groupId, isDefined(), matches(/^[a-z][a-z0-9_-]+(?:\.[a-z0-9_-]+)+[0-9a-z_-]$/));
ensure('artifactId', artifactId, isDefined(), matches(/^[a-z0-9_-]+$/));
ensure('version', version, isDefined(), matches(/^(?:\d+\.?){3}$/));
ensure('extension', extension, isDefined(), matches(/^[a-z]+$/));
}

toPath(): Path {
@@ -41,10 +41,3 @@ export class GAV extends TinyType {
return new Path(`${ name }.${ this.extension}`);
}
}

function matchesRegex(type: string, regex: RegExp): Predicate<string> {
return Predicate.to(`match ${type} regex ${regex}`, (value: string) =>
typeof value === 'string' &&
regex.test(value),
);
}
@@ -1,4 +1,5 @@
export * from './Complaint';
export * from './Credentials';
export * from './DownloadProgressReport';
export * from './ExecutionError';
export * from './GAV';

0 comments on commit c14ac29

Please sign in to comment.