Skip to content

Commit

Permalink
fix(import): improve handling of auth errors (#1051)
Browse files Browse the repository at this point in the history
no issue
- return more accurate error messages for authentication errors
  • Loading branch information
acburdine authored Nov 7, 2019
1 parent f540af7 commit fb6b431
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 32 deletions.
92 changes: 60 additions & 32 deletions lib/tasks/import/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,53 +39,81 @@ async function setup(version, url, data) {
await got.post('/authentication/setup/', {baseUrl, body, json: true});
}

function handleError(error) {
const {response} = error;
if (response.statusCode === 404) {
throw new SystemError({
message: 'There is no user with that email address.',
err: error
});
}

if (response.statusCode === 422) {
throw new SystemError({
message: 'Your password is incorrect.',
err: error
});
}

throw error;
}

async function getAuthOpts(version, url, {username, password}) {
const baseUrl = getBaseUrl(version, url);

if (semver.major(version) === 1) {
const {body: configBody} = await got('/configuration/', {baseUrl, json: true});
const {clientId, clientSecret} = get(configBody, 'configuration[0]', {});
const {body: authBody} = await got.post('/authentication/token/', {

try {
const {body: authBody} = await got.post('/authentication/token/', {
baseUrl,
json: true,
form: true,
body: {
grant_type: 'password',
client_id: clientId,
client_secret: clientSecret,
username,
password
}
});

return {
baseUrl,
headers: {
Authorization: `Bearer ${authBody.access_token}`
}
};
} catch (error) {
handleError(error);
}
}

try {
const {headers} = await got.post('/session/', {
baseUrl,
json: true,
form: true,
body: {
grant_type: 'password',
client_id: clientId,
client_secret: clientSecret,
username,
password
}
headers: {
Origin: url,
'Content-Type': 'application/json'
},
body: JSON.stringify({username, password})
});

/* istanbul ignore next */
const cookies = headers['set-cookie'] || [];
const filteredCookies = cookies.map(Cookie.parse).filter(Boolean).map(c => c.cookieString());

return {
baseUrl,
headers: {
Authorization: `Bearer ${authBody.access_token}`
Origin: url,
Cookie: filteredCookies
}
};
} catch (error) {
handleError(error);
}

const {headers} = await got.post('/session/', {
baseUrl,
headers: {
Origin: url,
'Content-Type': 'application/json'
},
body: JSON.stringify({username, password})
});

/* istanbul ignore next */
const cookies = headers['set-cookie'] || [];
const filteredCookies = cookies.map(Cookie.parse).filter(Boolean).map(c => c.cookieString());

return {
baseUrl,
headers: {
Origin: url,
Cookie: filteredCookies
}
};
}

async function runImport(version, url, auth, exportFile) {
Expand Down
118 changes: 118 additions & 0 deletions test/unit/tasks/import/api-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,54 @@ describe('Unit > Tasks > Import > setup', function () {
expect(importScope.isDone()).to.be.true;
});

it('handles 404 auth error', async function () {
const clientId = 'client-id';
const clientSecret = 'client-secret';
const configBody = {
configuration: [{
clientId, clientSecret
}]
};

const tokenRequestBody = {
grant_type: 'password',
client_id: clientId,
client_secret: clientSecret,
username: '[email protected]',
password: 'password'
};

const configScope = nock(testUrl)
.get('/ghost/api/v0.1/configuration/')
.reply(200, configBody);

const tokenScope = nock(testUrl)
.post('/ghost/api/v0.1/authentication/token/', tokenRequestBody)
.reply(404, {});

const importScope = nock(testUrl, {
reqheaders: {
Authorization: 'Bearer access-token'
}
}).post('/ghost/api/v0.1/db/').reply(200, {});

try {
await runImport('1.0.0', testUrl, {
username: '[email protected]',
password: 'password'
}, path.join(__dirname, 'fixtures/0.11.x.json'));
} catch (error) {
expect(error).to.be.an.instanceof(SystemError);
expect(error.message).to.equal('There is no user with that email address.');
expect(configScope.isDone()).to.be.true;
expect(tokenScope.isDone()).to.be.true;
expect(importScope.isDone()).to.be.false;
return;
}

expect.fail('runImport should have errored');
});

it('2.x', async function () {
const sessionScope = nock(testUrl, {
reqheaders: {
Expand Down Expand Up @@ -141,6 +189,41 @@ describe('Unit > Tasks > Import > setup', function () {
expect(importScope.isDone()).to.be.true;
});

it('handles 422 auth error', async function () {
const sessionScope = nock(testUrl, {
reqheaders: {
Origin: testUrl
}
}).post('/ghost/api/v2/admin/session/', {
username: '[email protected]',
password: 'password'
}).reply(422, 'Error');

const importScope = nock(testUrl, {
reqheaders: {
cookie: [
'ghost-admin-api-session=test-session-data'
],
origin: testUrl
}
}).post('/ghost/api/v2/admin/db/').reply(201, {});

try {
await runImport('2.0.0', 'http://localhost:2368', {
username: '[email protected]',
password: 'password'
}, path.join(__dirname, 'fixtures/2.x.json'));
} catch (error) {
expect(error).to.be.an.instanceof(SystemError);
expect(error.message).to.equal('Your password is incorrect.');
expect(sessionScope.isDone()).to.be.true;
expect(importScope.isDone()).to.be.false;
return;
}

expect.fail('runImport should have errored');
});

it('3.x', async function () {
const sessionScope = nock(testUrl, {
reqheaders: {
Expand Down Expand Up @@ -170,6 +253,41 @@ describe('Unit > Tasks > Import > setup', function () {
expect(sessionScope.isDone()).to.be.true;
expect(importScope.isDone()).to.be.true;
});

it('rethrows non-auth errors', async function () {
const sessionScope = nock(testUrl, {
reqheaders: {
Origin: testUrl
}
}).post('/ghost/api/v3/admin/session/', {
username: '[email protected]',
password: 'password'
}).reply(500, 'Error');

const importScope = nock(testUrl, {
reqheaders: {
cookie: [
'ghost-admin-api-session=test-session-data'
],
origin: testUrl
}
}).post('/ghost/api/v3/admin/db/').reply(201, {});

try {
await runImport('3.0.0', 'http://localhost:2368', {
username: '[email protected]',
password: 'password'
}, path.join(__dirname, 'fixtures/3.x.json'));
} catch (error) {
expect(error.response).to.exist;
expect(error.response.statusCode).to.equal(500);
expect(sessionScope.isDone()).to.be.true;
expect(importScope.isDone()).to.be.false;
return;
}

expect.fail('runImport should have errored');
});
});

describe('downloadExport', function () {
Expand Down

0 comments on commit fb6b431

Please sign in to comment.